Skip to content

Commit 7b6259b

Browse files
RubenCerna2079Copilotaaronburtle
authored
Adding 'Configure' Options to CLI for Azure Log Analytics (#2781)
## Why make this change? This change closes issue #2777 ## What is this change? This change extends the functionality of the `configure` CLI command by introducing support for Azure Log Analytics properties. With this enhancement, users can now configure the Azure Log Analytics properties inside of their config file without the need to directly edit it. This change also ensures that the validation of Azure Log Analytics works as intended. ## How was this tested? - [ ] Integration Tests - [X] Unit Tests ## Sample Request(s) CLI Updates Add support to dab configure: dab configure --runtime.telemetry.azure-log-analytics.enabled dab configure --runtime.telemetry.azure-log-analytics.auth.workspace-id dab configure --runtime.telemetry.azure-log-analytics.auth.dcr-immutable-id dab configure --runtime.telemetry.azure-log-analytics.auth.dce-endpoint dab configure --runtime.telemetry.azure-log-analytics.log-type dab configure --runtime.telemetry.azure-log-analytics.flush-interval-seconds --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: aaronburtle <[email protected]>
1 parent 79ffe37 commit 7b6259b

File tree

6 files changed

+214
-5
lines changed

6 files changed

+214
-5
lines changed

src/Cli.Tests/ConfigureOptionsTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,46 @@ public void TestAddAKVOptions()
148148
Assert.AreEqual(1, config.AzureKeyVault?.RetryPolicy.NetworkTimeoutSeconds);
149149
}
150150

151+
/// <summary>
152+
/// Tests that running the "configure --azure-log-analytics" commands on a config without Azure Log Analytics properties results
153+
/// in a valid config being generated.
154+
[TestMethod]
155+
public void TestAddAzureLogAnalyticsOptions()
156+
{
157+
// Arrange
158+
_fileSystem!.AddFile(TEST_RUNTIME_CONFIG_FILE, new MockFileData(INITIAL_CONFIG));
159+
160+
Assert.IsTrue(_fileSystem!.File.Exists(TEST_RUNTIME_CONFIG_FILE));
161+
162+
// Act: Attempts to add Azure Log Analytics options
163+
ConfigureOptions options = new(
164+
azureLogAnalyticsEnabled: CliBool.True,
165+
azureLogAnalyticsLogType: "log-type-test",
166+
azureLogAnalyticsFlushIntervalSeconds: 1,
167+
azureLogAnalyticsWorkspaceId: "workspace-id-test",
168+
azureLogAnalyticsDcrImmutableId: "dcr-immutable-id-test",
169+
azureLogAnalyticsDceEndpoint: "dce-endpoint-test",
170+
config: TEST_RUNTIME_CONFIG_FILE
171+
);
172+
173+
bool isSuccess = TryConfigureSettings(options, _runtimeConfigLoader!, _fileSystem!);
174+
175+
// Assert: Validate the Azure Log Analytics options are added.
176+
Assert.IsTrue(isSuccess);
177+
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
178+
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? config));
179+
Assert.IsNotNull(config.Runtime);
180+
Assert.IsNotNull(config.Runtime.Telemetry);
181+
Assert.IsNotNull(config.Runtime.Telemetry.AzureLogAnalytics);
182+
Assert.AreEqual(true, config.Runtime.Telemetry.AzureLogAnalytics.Enabled);
183+
Assert.AreEqual("log-type-test", config.Runtime.Telemetry.AzureLogAnalytics.LogType);
184+
Assert.AreEqual(1, config.Runtime.Telemetry.AzureLogAnalytics.FlushIntervalSeconds);
185+
Assert.IsNotNull(config.Runtime.Telemetry.AzureLogAnalytics.Auth);
186+
Assert.AreEqual("workspace-id-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.WorkspaceId);
187+
Assert.AreEqual("dcr-immutable-id-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.DcrImmutableId);
188+
Assert.AreEqual("dce-endpoint-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.DceEndpoint);
189+
}
190+
151191
/// <summary>
152192
/// Tests that running "dab configure --runtime.graphql.enabled" on a config with various values results
153193
/// in runtime. Takes in updated value for graphql.enabled and

src/Cli.Tests/ValidateConfigTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,39 @@ public async Task TestValidateAKVOptionsWithoutEndpointFails()
311311
JsonSchemaValidationResult result = await validator.ValidateConfigSchema(config, TEST_RUNTIME_CONFIG_FILE, mockLoggerFactory.Object);
312312
Assert.IsFalse(result.IsValid);
313313
}
314+
315+
/// <summary>
316+
/// Tests that validation fails when Azure Log Analytics options are configured without the Auth options.
317+
/// </summary>
318+
[TestMethod]
319+
public async Task TestValidateAzureLogAnalyticsOptionsWithoutAuthFails()
320+
{
321+
// Arrange
322+
_fileSystem!.AddFile(TEST_RUNTIME_CONFIG_FILE, new MockFileData(INITIAL_CONFIG));
323+
Assert.IsTrue(_fileSystem!.File.Exists(TEST_RUNTIME_CONFIG_FILE));
324+
Mock<RuntimeConfigProvider> mockRuntimeConfigProvider = new(_runtimeConfigLoader);
325+
RuntimeConfigValidator validator = new(mockRuntimeConfigProvider.Object, _fileSystem, new Mock<ILogger<RuntimeConfigValidator>>().Object);
326+
Mock<ILoggerFactory> mockLoggerFactory = new();
327+
Mock<ILogger<JsonConfigSchemaValidator>> mockLogger = new();
328+
mockLoggerFactory
329+
.Setup(factory => factory.CreateLogger(typeof(JsonConfigSchemaValidator).FullName!))
330+
.Returns(mockLogger.Object);
331+
332+
// Act: Attempts to add Azure Log Analytics options without Auth options
333+
ConfigureOptions options = new(
334+
azureLogAnalyticsEnabled: CliBool.True,
335+
azureLogAnalyticsLogType: "log-type-test",
336+
azureLogAnalyticsFlushIntervalSeconds: 1,
337+
config: TEST_RUNTIME_CONFIG_FILE
338+
);
339+
340+
bool isSuccess = TryConfigureSettings(options, _runtimeConfigLoader!, _fileSystem!);
341+
342+
// Assert: Settings are configured, config parses, validation fails.
343+
Assert.IsTrue(isSuccess);
344+
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
345+
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? config));
346+
JsonSchemaValidationResult result = await validator.ValidateConfigSchema(config, TEST_RUNTIME_CONFIG_FILE, mockLoggerFactory.Object);
347+
Assert.IsFalse(result.IsValid);
348+
}
314349
}

src/Cli/Commands/ConfigureOptions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ public ConfigureOptions(
4848
int? azureKeyVaultRetryPolicyDelaySeconds = null,
4949
int? azureKeyVaultRetryPolicyMaxDelaySeconds = null,
5050
int? azureKeyVaultRetryPolicyNetworkTimeoutSeconds = null,
51+
CliBool? azureLogAnalyticsEnabled = null,
52+
string? azureLogAnalyticsLogType = null,
53+
int? azureLogAnalyticsFlushIntervalSeconds = null,
54+
string? azureLogAnalyticsWorkspaceId = null,
55+
string? azureLogAnalyticsDcrImmutableId = null,
56+
string? azureLogAnalyticsDceEndpoint = null,
5157
string? config = null)
5258
: base(config)
5359
{
@@ -85,6 +91,13 @@ public ConfigureOptions(
8591
AzureKeyVaultRetryPolicyDelaySeconds = azureKeyVaultRetryPolicyDelaySeconds;
8692
AzureKeyVaultRetryPolicyMaxDelaySeconds = azureKeyVaultRetryPolicyMaxDelaySeconds;
8793
AzureKeyVaultRetryPolicyNetworkTimeoutSeconds = azureKeyVaultRetryPolicyNetworkTimeoutSeconds;
94+
// Azure Log Analytics
95+
AzureLogAnalyticsEnabled = azureLogAnalyticsEnabled;
96+
AzureLogAnalyticsLogType = azureLogAnalyticsLogType;
97+
AzureLogAnalyticsFlushIntervalSeconds = azureLogAnalyticsFlushIntervalSeconds;
98+
AzureLogAnalyticsWorkspaceId = azureLogAnalyticsWorkspaceId;
99+
AzureLogAnalyticsDcrImmutableId = azureLogAnalyticsDcrImmutableId;
100+
AzureLogAnalyticsDceEndpoint = azureLogAnalyticsDceEndpoint;
88101
}
89102

90103
[Option("data-source.database-type", Required = false, HelpText = "Database type. Allowed values: MSSQL, PostgreSQL, CosmosDB_NoSQL, MySQL.")]
@@ -171,6 +184,24 @@ public ConfigureOptions(
171184
[Option("azure-key-vault.retry-policy.network-timeout-seconds", Required = false, HelpText = "Configure the network timeout for requests in seconds. Default: 60.")]
172185
public int? AzureKeyVaultRetryPolicyNetworkTimeoutSeconds { get; }
173186

187+
[Option("runtime.telemetry.azure-log-analytics.enabled", Default = CliBool.False, Required = false, HelpText = "Enable/Disable Azure Log Analytics.")]
188+
public CliBool? AzureLogAnalyticsEnabled { get; }
189+
190+
[Option("runtime.telemetry.azure-log-analytics.log-type", Required = false, HelpText = "Configure Log Type for Azure Log Analytics to find table to send telemetry data")]
191+
public string? AzureLogAnalyticsLogType { get; }
192+
193+
[Option("runtime.telemetry.azure-log-analytics.flush-interval-seconds", Required = false, HelpText = "Configure Flush Interval in seconds for Azure Log Analytics to specify the time interval to send the telemetry data")]
194+
public int? AzureLogAnalyticsFlushIntervalSeconds { get; }
195+
196+
[Option("runtime.telemetry.azure-log-analytics.auth.workspace-id", Required = false, HelpText = "Configure Workspace ID for Azure Log Analytics used to find workspace to connect")]
197+
public string? AzureLogAnalyticsWorkspaceId { get; }
198+
199+
[Option("runtime.telemetry.azure-log-analytics.auth.dcr-immutable-id", Required = false, HelpText = "Configure DCR Immutable ID for Azure Log Analytics to find the data collection rule that defines how data is collected")]
200+
public string? AzureLogAnalyticsDcrImmutableId { get; }
201+
202+
[Option("runtime.telemetry.azure-log-analytics.auth.dce-endpoint", Required = false, HelpText = "Configure DCE Endpoint for Azure Log Analytics to find table to send telemetry data")]
203+
public string? AzureLogAnalyticsDceEndpoint { get; }
204+
174205
public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
175206
{
176207
logger.LogInformation("{productName} {version}", PRODUCT_NAME, ProductInfo.GetProductVersion());

src/Cli/ConfigGenerator.cs

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,26 @@ private static bool TryUpdateConfiguredRuntimeOptions(
778778
}
779779
}
780780

781+
// Telemetry: Azure Log Analytics
782+
if (options.AzureLogAnalyticsEnabled is not null ||
783+
options.AzureLogAnalyticsLogType is not null ||
784+
options.AzureLogAnalyticsFlushIntervalSeconds is not null ||
785+
options.AzureLogAnalyticsWorkspaceId is not null ||
786+
options.AzureLogAnalyticsDcrImmutableId is not null ||
787+
options.AzureLogAnalyticsDceEndpoint is not null)
788+
{
789+
AzureLogAnalyticsOptions updatedAzureLogAnalyticsOptions = runtimeConfig?.Runtime?.Telemetry?.AzureLogAnalytics ?? new();
790+
bool status = TryUpdateConfiguredAzureLogAnalyticsOptions(options, ref updatedAzureLogAnalyticsOptions);
791+
if (status)
792+
{
793+
runtimeConfig = runtimeConfig! with { Runtime = runtimeConfig.Runtime! with { Telemetry = runtimeConfig.Runtime!.Telemetry is not null ? runtimeConfig.Runtime!.Telemetry with { AzureLogAnalytics = updatedAzureLogAnalyticsOptions } : new TelemetryOptions(AzureLogAnalytics: updatedAzureLogAnalyticsOptions) } };
794+
}
795+
else
796+
{
797+
return false;
798+
}
799+
}
800+
781801
return runtimeConfig != null;
782802
}
783803

@@ -844,7 +864,7 @@ private static bool TryUpdateConfiguredRestValues(ConfigureOptions options, ref
844864
/// </summary>
845865
/// <param name="options">options.</param>
846866
/// <param name="updatedGraphQLOptions">updatedGraphQLOptions.</param>
847-
/// <returns>True if the value needs to be udpated in the runtime config, else false</returns>
867+
/// <returns>True if the value needs to be updated in the runtime config, else false</returns>
848868
private static bool TryUpdateConfiguredGraphQLValues(
849869
ConfigureOptions options,
850870
ref GraphQLRuntimeOptions? updatedGraphQLOptions)
@@ -910,7 +930,7 @@ private static bool TryUpdateConfiguredGraphQLValues(
910930
/// </summary>
911931
/// <param name="options">options.</param>
912932
/// <param name="updatedCacheOptions">updatedCacheOptions.</param>
913-
/// <returns>True if the value needs to be udpated in the runtime config, else false</returns>
933+
/// <returns>True if the value needs to be updated in the runtime config, else false</returns>
914934
private static bool TryUpdateConfiguredCacheValues(
915935
ConfigureOptions options,
916936
ref RuntimeCacheOptions? updatedCacheOptions)
@@ -959,7 +979,7 @@ private static bool TryUpdateConfiguredCacheValues(
959979
/// </summary>
960980
/// <param name="options">options.</param>
961981
/// <param name="updatedHostOptions">updatedHostOptions.</param>
962-
/// <returns>True if the value needs to be udpated in the runtime config, else false</returns>
982+
/// <returns>True if the value needs to be updated in the runtime config, else false</returns>
963983
private static bool TryUpdateConfiguredHostValues(
964984
ConfigureOptions options,
965985
ref HostOptions? updatedHostOptions)
@@ -1095,6 +1115,90 @@ private static bool TryUpdateConfiguredHostValues(
10951115
}
10961116
}
10971117

1118+
/// <summary>
1119+
/// Attempts to update the Azure Log Analytics configuration options based on the provided values.
1120+
/// Validates that any user-provided parameter value is valid and updates the runtime configuration accordingly.
1121+
/// </summary>
1122+
/// <param name="options">The configuration options provided by the user.</param>
1123+
/// <param name="azureLogAnalyticsOptions">The Azure Log Analytics options to be updated.</param>
1124+
/// <returns>True if the Azure Log Analytics options were successfully configured; otherwise, false.</returns>
1125+
private static bool TryUpdateConfiguredAzureLogAnalyticsOptions(
1126+
ConfigureOptions options,
1127+
ref AzureLogAnalyticsOptions azureLogAnalyticsOptions)
1128+
{
1129+
try
1130+
{
1131+
AzureLogAnalyticsAuthOptions? updatedAuthOptions = azureLogAnalyticsOptions.Auth;
1132+
1133+
// Runtime.Telemetry.AzureLogAnalytics.Enabled
1134+
if (options.AzureLogAnalyticsEnabled is not null)
1135+
{
1136+
azureLogAnalyticsOptions = azureLogAnalyticsOptions with { Enabled = options.AzureLogAnalyticsEnabled is CliBool.True, UserProvidedEnabled = true };
1137+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.enabled as '{options.AzureLogAnalyticsEnabled}'");
1138+
}
1139+
1140+
// Runtime.Telemetry.AzureLogAnalytics.LogType
1141+
if (options.AzureLogAnalyticsLogType is not null)
1142+
{
1143+
azureLogAnalyticsOptions = azureLogAnalyticsOptions with { LogType = options.AzureLogAnalyticsLogType, UserProvidedLogType = true };
1144+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.log-type as '{options.AzureLogAnalyticsLogType}'");
1145+
}
1146+
1147+
// Runtime.Telemetry.AzureLogAnalytics.FlushIntervalSeconds
1148+
if (options.AzureLogAnalyticsFlushIntervalSeconds is not null)
1149+
{
1150+
if (options.AzureLogAnalyticsFlushIntervalSeconds <= 0)
1151+
{
1152+
_logger.LogError("Failed to update configuration with runtime.telemetry.azure-log-analytics.flush-interval-seconds. Value must be a positive integer greater than 0.");
1153+
return false;
1154+
}
1155+
1156+
azureLogAnalyticsOptions = azureLogAnalyticsOptions with { FlushIntervalSeconds = options.AzureLogAnalyticsFlushIntervalSeconds, UserProvidedFlushIntervalSeconds = true };
1157+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.flush-interval-seconds as '{options.AzureLogAnalyticsFlushIntervalSeconds}'");
1158+
}
1159+
1160+
// Runtime.Telemetry.AzureLogAnalytics.Auth.WorkspaceId
1161+
if (options.AzureLogAnalyticsWorkspaceId is not null)
1162+
{
1163+
updatedAuthOptions = updatedAuthOptions is not null
1164+
? updatedAuthOptions with { WorkspaceId = options.AzureLogAnalyticsWorkspaceId, UserProvidedWorkspaceId = true }
1165+
: new AzureLogAnalyticsAuthOptions { WorkspaceId = options.AzureLogAnalyticsWorkspaceId, UserProvidedWorkspaceId = true };
1166+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.auth.workspace-id as '{options.AzureLogAnalyticsWorkspaceId}'");
1167+
}
1168+
1169+
// Runtime.Telemetry.AzureLogAnalytics.Auth.DcrImmutableId
1170+
if (options.AzureLogAnalyticsDcrImmutableId is not null)
1171+
{
1172+
updatedAuthOptions = updatedAuthOptions is not null
1173+
? updatedAuthOptions with { DcrImmutableId = options.AzureLogAnalyticsDcrImmutableId, UserProvidedDcrImmutableId = true }
1174+
: new AzureLogAnalyticsAuthOptions { DcrImmutableId = options.AzureLogAnalyticsDcrImmutableId, UserProvidedDcrImmutableId = true };
1175+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.auth.dcr-immutable-id as '{options.AzureLogAnalyticsDcrImmutableId}'");
1176+
}
1177+
1178+
// Runtime.Telemetry.AzureLogAnalytics.Auth.DceEndpoint
1179+
if (options.AzureLogAnalyticsDceEndpoint is not null)
1180+
{
1181+
updatedAuthOptions = updatedAuthOptions is not null
1182+
? updatedAuthOptions with { DceEndpoint = options.AzureLogAnalyticsDceEndpoint, UserProvidedDceEndpoint = true }
1183+
: new AzureLogAnalyticsAuthOptions { DceEndpoint = options.AzureLogAnalyticsDceEndpoint, UserProvidedDceEndpoint = true };
1184+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.auth.dce-endpoint as '{options.AzureLogAnalyticsDceEndpoint}'");
1185+
}
1186+
1187+
// Update Azure Log Analytics options with Auth options if it was modified
1188+
if (updatedAuthOptions is not null)
1189+
{
1190+
azureLogAnalyticsOptions = azureLogAnalyticsOptions with { Auth = updatedAuthOptions };
1191+
}
1192+
1193+
return true;
1194+
}
1195+
catch (Exception ex)
1196+
{
1197+
_logger.LogError($"Failed to update configuration with runtime.telemetry.azure-log-analytics. Exception message: {ex.Message}.");
1198+
return false;
1199+
}
1200+
}
1201+
10981202
/// <summary>
10991203
/// Parse permission string to create PermissionSetting array.
11001204
/// </summary>

src/Config/Converters/AzureLogAnalyticsAuthOptionsConverter.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ public AzureLogAnalyticsAuthOptionsConverter(bool replaceEnvVar)
7373
throw new JsonException($"Unexpected property {propertyName}");
7474
}
7575
}
76-
7776
}
7877

7978
throw new JsonException("Failed to read the Azure Log Analytics Auth Options");

src/Config/Converters/AzureLogAnalyticsOptionsConverterFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public override void Write(Utf8JsonWriter writer, AzureLogAnalyticsOptions value
141141
JsonSerializer.Serialize(writer, value.Enabled, options);
142142
}
143143

144-
if (value?.Auth is not null)
144+
if (value?.Auth is not null && (value.Auth.UserProvidedWorkspaceId || value.Auth.UserProvidedDcrImmutableId || value.Auth.UserProvidedDceEndpoint))
145145
{
146146
AzureLogAnalyticsAuthOptionsConverter authOptionsConverter = options.GetConverter(typeof(AzureLogAnalyticsAuthOptions)) as AzureLogAnalyticsAuthOptionsConverter ??
147147
throw new JsonException("Failed to get azure-log-analytics.auth options converter");

0 commit comments

Comments
 (0)