Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
125 changes: 125 additions & 0 deletions schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,54 @@
}
}
},
"azure-key-vault": {
"type": "object",
"description": "Azure Key Vault configuration for storing secrets",
"additionalProperties": false,
"properties": {
"endpoint": {
"type": "string",
"description": "Azure Key Vault endpoint URL"
},
"retry-policy": {
"type": "object",
"description": "Retry policy configuration for Azure Key Vault operations",
"additionalProperties": false,
"properties": {
"mode": {
"type": "string",
"enum": ["fixed", "exponential"],
"default": "exponential",
"description": "Retry mode: fixed or exponential backoff"
},
"max-count": {
"type": "integer",
"default": 3,
"minimum": 1,
"description": "Maximum number of retry attempts"
},
"delay-seconds": {
"type": "integer",
"default": 1,
"minimum": 1,
"description": "Initial delay between retries in seconds"
},
"max-delay-seconds": {
"type": "integer",
"default": 60,
"minimum": 1,
"description": "Maximum delay between retries in seconds (for exponential mode)"
},
"network-timeout-seconds": {
"type": "integer",
"default": 60,
"minimum": 1,
"description": "Network timeout for requests in seconds"
}
}
}
}
},
"entities": {
"type": "object",
"description": "Entities that will be exposed via REST and/or GraphQL",
Expand Down Expand Up @@ -874,6 +922,83 @@
}
}
},
"allOf": [
{
"if": {
"properties": {
"azure-key-vault": {
"type": "object",
"properties": {
"retry-policy": {
"type": "object"
}
},
"required": ["retry-policy"]
}
},
"required": ["azure-key-vault"]
},
"then": {
"properties": {
"azure-key-vault": {
"properties": {
"endpoint": {
"type": "string"
}
},
"required": ["endpoint"]
}
}
}
},
{
"if": {
"properties": {
"azure-key-vault": {
"type": "object",
"properties": {
"retry-policy": {
"type": "object",
"properties": {
"mode": {
"const": "exponential"
}
}
}
}
}
}
},
"then": {
"properties": {
"azure-key-vault": {
"properties": {
"retry-policy": {
"properties": {
"max-delay-seconds": {
"type": "integer"
}
}
}
}
}
}
},
"else": {
"properties": {
"azure-key-vault": {
"properties": {
"retry-policy": {
"not": {
"required": ["max-delay-seconds"]
}
}
}
}
}
}
}
],
"required": ["data-source", "entities"],
"$defs": {
"singular-plural": {
Expand Down
31 changes: 31 additions & 0 deletions src/Cli/Commands/ConfigureOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public ConfigureOptions(
string? runtimeHostAuthenticationProvider = null,
string? runtimeHostAuthenticationJwtAudience = null,
string? runtimeHostAuthenticationJwtIssuer = null,
string? azureKeyVaultEndpoint = null,
RetryPolicyMode? azureKeyVaultRetryPolicyMode = null,
int? azureKeyVaultRetryPolicyMaxCount = null,
int? azureKeyVaultRetryPolicyDelaySeconds = null,
int? azureKeyVaultRetryPolicyMaxDelaySeconds = null,
int? azureKeyVaultRetryPolicyNetworkTimeoutSeconds = null,
string? config = null)
: base(config)
{
Expand Down Expand Up @@ -72,6 +78,13 @@ public ConfigureOptions(
RuntimeHostAuthenticationProvider = runtimeHostAuthenticationProvider;
RuntimeHostAuthenticationJwtAudience = runtimeHostAuthenticationJwtAudience;
RuntimeHostAuthenticationJwtIssuer = runtimeHostAuthenticationJwtIssuer;
// Azure Key Vault
AzureKeyVaultEndpoint = azureKeyVaultEndpoint;
AzureKeyVaultRetryPolicyMode = azureKeyVaultRetryPolicyMode;
AzureKeyVaultRetryPolicyMaxCount = azureKeyVaultRetryPolicyMaxCount;
AzureKeyVaultRetryPolicyDelaySeconds = azureKeyVaultRetryPolicyDelaySeconds;
AzureKeyVaultRetryPolicyMaxDelaySeconds = azureKeyVaultRetryPolicyMaxDelaySeconds;
AzureKeyVaultRetryPolicyNetworkTimeoutSeconds = azureKeyVaultRetryPolicyNetworkTimeoutSeconds;
}

[Option("data-source.database-type", Required = false, HelpText = "Database type. Allowed values: MSSQL, PostgreSQL, CosmosDB_NoSQL, MySQL.")]
Expand Down Expand Up @@ -140,6 +153,24 @@ public ConfigureOptions(
[Option("runtime.host.authentication.jwt.issuer", Required = false, HelpText = "Configure the entity that issued the Jwt Token.")]
public string? RuntimeHostAuthenticationJwtIssuer { get; }

[Option("azure-key-vault.endpoint", Required = false, HelpText = "Configure the Azure Key Vault endpoint URL.")]
public string? AzureKeyVaultEndpoint { get; }

[Option("azure-key-vault.retry-policy.mode", Required = false, HelpText = "Configure the retry policy mode. Allowed values: fixed, exponential. Default: exponential.")]
public RetryPolicyMode? AzureKeyVaultRetryPolicyMode { get; }

[Option("azure-key-vault.retry-policy.max-count", Required = false, HelpText = "Configure the maximum number of retry attempts. Default: 3.")]
public int? AzureKeyVaultRetryPolicyMaxCount { get; }

[Option("azure-key-vault.retry-policy.delay-seconds", Required = false, HelpText = "Configure the initial delay between retries in seconds. Default: 1.")]
public int? AzureKeyVaultRetryPolicyDelaySeconds { get; }

[Option("azure-key-vault.retry-policy.max-delay-seconds", Required = false, HelpText = "Configure the maximum delay between retries in seconds (for exponential mode). Default: 60.")]
public int? AzureKeyVaultRetryPolicyMaxDelaySeconds { get; }

[Option("azure-key-vault.retry-policy.network-timeout-seconds", Required = false, HelpText = "Configure the network timeout for requests in seconds. Default: 60.")]
public int? AzureKeyVaultRetryPolicyNetworkTimeoutSeconds { get; }

public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
{
logger.LogInformation("{productName} {version}", PRODUCT_NAME, ProductInfo.GetProductVersion());
Expand Down
122 changes: 122 additions & 0 deletions src/Cli/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,11 @@ public static bool TryConfigureSettings(ConfigureOptions options, FileSystemRunt
return false;
}

if (!TryUpdateConfiguredAzureKeyVaultOptions(options, ref runtimeConfig))
{
return false;
}

return WriteRuntimeConfigToFile(runtimeConfigFile, runtimeConfig, fileSystem);
}

Expand Down Expand Up @@ -1990,5 +1995,122 @@ public static bool TryAddTelemetry(AddTelemetryOptions options, FileSystemRuntim

return WriteRuntimeConfigToFile(runtimeConfigFile, runtimeConfig, fileSystem);
}

/// <summary>
/// Attempts to update the Azure Key Vault configuration options based on the provided values.
/// Validates that any user-provided parameter value is valid and updates the runtime configuration accordingly.
/// </summary>
/// <param name="options">The configuration options provided by the user.</param>
/// <param name="runtimeConfig">The runtime configuration to be updated.</param>
/// <returns>True if the Azure Key Vault options were successfully configured; otherwise, false.</returns>
private static bool TryUpdateConfiguredAzureKeyVaultOptions(
ConfigureOptions options,
[NotNullWhen(true)] ref RuntimeConfig runtimeConfig)
{
try
{
AzureKeyVaultOptions? updatedAzureKeyVaultOptions = runtimeConfig.AzureKeyVault;
RetryPolicyOptions? updatedRetryPolicyOptions = updatedAzureKeyVaultOptions?.RetryPolicy;

// Azure Key Vault Endpoint
if (options.AzureKeyVaultEndpoint is not null)
{
updatedAzureKeyVaultOptions = updatedAzureKeyVaultOptions is not null
? updatedAzureKeyVaultOptions with { Endpoint = options.AzureKeyVaultEndpoint }
: new AzureKeyVaultOptions { Endpoint = options.AzureKeyVaultEndpoint };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.endpoint as '{endpoint}'", options.AzureKeyVaultEndpoint);
}

// Retry Policy Mode
if (options.AzureKeyVaultRetryPolicyMode is not null)
{
updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
? updatedRetryPolicyOptions with { Mode = options.AzureKeyVaultRetryPolicyMode.Value }
: new RetryPolicyOptions { Mode = options.AzureKeyVaultRetryPolicyMode.Value };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.mode as '{mode}'", options.AzureKeyVaultRetryPolicyMode.Value);
}

// Retry Policy Max Count
if (options.AzureKeyVaultRetryPolicyMaxCount is not null)
{
if (options.AzureKeyVaultRetryPolicyMaxCount.Value < 1)
{
_logger.LogError("Failed to update azure-key-vault.retry-policy.max-count. Value must be at least 1.");
return false;
}

updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
? updatedRetryPolicyOptions with { MaxCount = options.AzureKeyVaultRetryPolicyMaxCount.Value }
: new RetryPolicyOptions { MaxCount = options.AzureKeyVaultRetryPolicyMaxCount.Value };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.max-count as '{maxCount}'", options.AzureKeyVaultRetryPolicyMaxCount.Value);
}

// Retry Policy Delay Seconds
if (options.AzureKeyVaultRetryPolicyDelaySeconds is not null)
{
if (options.AzureKeyVaultRetryPolicyDelaySeconds.Value < 1)
{
_logger.LogError("Failed to update azure-key-vault.retry-policy.delay-seconds. Value must be at least 1.");
return false;
}

updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
? updatedRetryPolicyOptions with { DelaySeconds = options.AzureKeyVaultRetryPolicyDelaySeconds.Value }
: new RetryPolicyOptions { DelaySeconds = options.AzureKeyVaultRetryPolicyDelaySeconds.Value };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.delay-seconds as '{delaySeconds}'", options.AzureKeyVaultRetryPolicyDelaySeconds.Value);
}

// Retry Policy Max Delay Seconds
if (options.AzureKeyVaultRetryPolicyMaxDelaySeconds is not null)
{
if (options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value < 1)
{
_logger.LogError("Failed to update azure-key-vault.retry-policy.max-delay-seconds. Value must be at least 1.");
return false;
}

updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
? updatedRetryPolicyOptions with { MaxDelaySeconds = options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value }
: new RetryPolicyOptions { MaxDelaySeconds = options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.max-delay-seconds as '{maxDelaySeconds}'", options.AzureKeyVaultRetryPolicyMaxDelaySeconds.Value);
}

// Retry Policy Network Timeout Seconds
if (options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds is not null)
{
if (options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value < 1)
{
_logger.LogError("Failed to update azure-key-vault.retry-policy.network-timeout-seconds. Value must be at least 1.");
return false;
}

updatedRetryPolicyOptions = updatedRetryPolicyOptions is not null
? updatedRetryPolicyOptions with { NetworkTimeoutSeconds = options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value }
: new RetryPolicyOptions { NetworkTimeoutSeconds = options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value };
_logger.LogInformation("Updated RuntimeConfig with azure-key-vault.retry-policy.network-timeout-seconds as '{networkTimeoutSeconds}'", options.AzureKeyVaultRetryPolicyNetworkTimeoutSeconds.Value);
}

// Update Azure Key Vault options with retry policy if retry policy was modified
if (updatedRetryPolicyOptions is not null)
{
updatedAzureKeyVaultOptions = updatedAzureKeyVaultOptions is not null
? updatedAzureKeyVaultOptions with { RetryPolicy = updatedRetryPolicyOptions }
: new AzureKeyVaultOptions { RetryPolicy = updatedRetryPolicyOptions };
}

// Update runtime config if Azure Key Vault options were modified
if (updatedAzureKeyVaultOptions is not null)
{
runtimeConfig = runtimeConfig with { AzureKeyVault = updatedAzureKeyVaultOptions };
}

return true;
}
catch (Exception ex)
{
_logger.LogError("Failed to update RuntimeConfig.AzureKeyVault with exception message: {exceptionMessage}.", ex.Message);
return false;
}
}
}
}
15 changes: 15 additions & 0 deletions src/Config/ObjectModel/AzureKeyVaultOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Azure.DataApiBuilder.Config.ObjectModel;

public record AzureKeyVaultOptions
{
[JsonPropertyName("endpoint")]
public string? Endpoint { get; init; }

[JsonPropertyName("retry-policy")]
public RetryPolicyOptions? RetryPolicy { get; init; }
}
13 changes: 13 additions & 0 deletions src/Config/ObjectModel/RetryPolicyMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Azure.DataApiBuilder.Config.ObjectModel;

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum RetryPolicyMode
{
Fixed,
Exponential
}
24 changes: 24 additions & 0 deletions src/Config/ObjectModel/RetryPolicyOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Azure.DataApiBuilder.Config.ObjectModel;

public record RetryPolicyOptions
{
[JsonPropertyName("mode")]
public RetryPolicyMode Mode { get; init; } = RetryPolicyMode.Exponential;

[JsonPropertyName("max-count")]
public int MaxCount { get; init; } = 3;

[JsonPropertyName("delay-seconds")]
public int DelaySeconds { get; init; } = 1;

[JsonPropertyName("max-delay-seconds")]
public int MaxDelaySeconds { get; init; } = 60;

[JsonPropertyName("network-timeout-seconds")]
public int NetworkTimeoutSeconds { get; init; } = 60;
}
Loading
Loading