Skip to content

Commit 6064813

Browse files
committed
Add nuke step CheckConfigurationKeysAgainstJsonValidations to check if ConfigurationKeys class doesnt list config keys not present in the json
1 parent d72acce commit 6064813

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

tracer/build/_build/Build.Steps.cs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Net.Http;
6+
using System.Reflection;
67
using System.Runtime.InteropServices;
78
using System.Text;
89
using System.Text.Json;
@@ -2574,6 +2575,172 @@ string NormalizedPath(AbsolutePath ap)
25742575
await LogParser.ReportNativeMetrics(logDirectory);
25752576
});
25762577

2578+
public Target CheckConfigurationKeysAgainstJsonValidations => _ => _
2579+
.Description("Validates that all ConfigurationKeys constants are present in supported-configurations.json")
2580+
.After(CompileManagedSrc)
2581+
.Executes(() =>
2582+
{
2583+
var jsonPath = TracerDirectory / "src" / "Datadog.Trace" / "Configuration" / "supported-configurations.json";
2584+
if (!File.Exists(jsonPath))
2585+
{
2586+
Logger.Error("supported-configurations.json file not found at {JsonPath}", jsonPath);
2587+
return;
2588+
}
2589+
2590+
// Parse JSON to get supported configuration keys
2591+
var jsonContent = File.ReadAllText(jsonPath);
2592+
var jsonDoc = JsonDocument.Parse(jsonContent);
2593+
var canonicalKeysInJson = new HashSet<string>();
2594+
var aliasesInJson = new HashSet<string>();
2595+
var deprecationsInJson = new HashSet<string>();
2596+
2597+
// Add keys from supportedConfigurations section
2598+
if (jsonDoc.RootElement.TryGetProperty("supportedConfigurations", out var supportedConfigs))
2599+
{
2600+
foreach (var property in supportedConfigs.EnumerateObject())
2601+
{
2602+
canonicalKeysInJson.Add(property.Name);
2603+
deprecationsInJson.Add(property.Name);
2604+
}
2605+
}
2606+
2607+
// Add keys from aliases section (add elements in the arrays)
2608+
if (jsonDoc.RootElement.TryGetProperty("aliases", out var aliases))
2609+
{
2610+
foreach (var property in aliases.EnumerateObject())
2611+
{
2612+
// Add all alias keys from the array
2613+
if (property.Value.ValueKind == JsonValueKind.Array)
2614+
{
2615+
foreach (var aliasElement in property.Value.EnumerateArray())
2616+
{
2617+
if (aliasElement.ValueKind == JsonValueKind.String)
2618+
{
2619+
aliasesInJson.Add(aliasElement.GetString());
2620+
}
2621+
}
2622+
}
2623+
}
2624+
}
2625+
2626+
// Add keys from deprecations section (add the deprecated keys themselves)
2627+
if (jsonDoc.RootElement.TryGetProperty("deprecations", out var deprecations))
2628+
{
2629+
foreach (var property in deprecations.EnumerateObject())
2630+
{
2631+
deprecationsInJson.Add(property.Name);
2632+
}
2633+
}
2634+
2635+
// Load the net6.0 assembly for configuration key analysis
2636+
// Configuration keys are the same across all frameworks, so we only need one
2637+
const string targetFramework = "net6.0";
2638+
var assemblyPath = TracerDirectory / "src" / "Datadog.Trace" / "bin" / BuildConfiguration / targetFramework / "Datadog.Trace.dll";
2639+
2640+
if (!File.Exists(assemblyPath))
2641+
{
2642+
throw new InvalidOperationException($"Assembly not found at {assemblyPath} for framework {targetFramework}. Make sure CompileManagedSrc has run.");
2643+
}
2644+
2645+
Assembly tracerAssembly;
2646+
try
2647+
{
2648+
// Load the assembly directly - no complex resolution needed
2649+
tracerAssembly = Assembly.LoadFrom(assemblyPath);
2650+
Logger.Information("Using {Framework} assembly for configuration key analysis", targetFramework);
2651+
}
2652+
catch (Exception ex)
2653+
{
2654+
throw new InvalidOperationException($"Failed to load assembly for {targetFramework}: {ex.Message}", ex);
2655+
}
2656+
2657+
// Use reflection to get all configuration keys from the ConfigurationKeys class
2658+
var configurationKeysInCode = new HashSet<string>();
2659+
var keyToFieldMap = new Dictionary<string, string>();
2660+
2661+
const string configKeysClassName = "Datadog.Trace.Configuration.ConfigurationKeys";
2662+
// Get the ConfigurationKeys type (includes all partial classes)
2663+
var configKeysType = tracerAssembly.GetType(configKeysClassName);
2664+
if (configKeysType == null)
2665+
{
2666+
throw new InvalidOperationException($"Could not find {configKeysClassName} type in assembly for framework {targetFramework}");
2667+
}
2668+
2669+
// Get all public const string fields from the type and all nested types (one level deep)
2670+
var allTypes = new List<Type> { configKeysType };
2671+
allTypes.AddRange(configKeysType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic));
2672+
2673+
Logger.Information("Found {TypeCount} types to analyze (including nested classes)", allTypes.Count);
2674+
2675+
foreach (var type in allTypes)
2676+
{
2677+
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)
2678+
.Where(f => f.IsLiteral && !f.IsInitOnly && f.FieldType == typeof(string));
2679+
2680+
Logger.Information(" Type {TypeName} has {FieldCount} const string fields", type.Name, fields.Count());
2681+
2682+
foreach (var field in fields)
2683+
{
2684+
var value = (string)field.GetValue(null)!;
2685+
configurationKeysInCode.Add(value);
2686+
2687+
// Build a more descriptive path for nested classes
2688+
var typeName = type.DeclaringType != null ? $"ConfigurationKeys.{type.Name}" : "ConfigurationKeys";
2689+
keyToFieldMap[value] = $"{typeName}.{field.Name}";
2690+
}
2691+
}
2692+
2693+
Logger.Information("Found {KeyCount} configuration keys in {Framework}", configurationKeysInCode.Count, targetFramework);
2694+
2695+
if (!configurationKeysInCode.Any())
2696+
{
2697+
throw new InvalidOperationException($"Could not extract any configuration keys from {targetFramework} framework");
2698+
}
2699+
2700+
var canonicalAndDeprecationsInJson = canonicalKeysInJson.Concat(deprecationsInJson);
2701+
// Find keys that are in JSON but not defined in ConfigurationKeys
2702+
var missingFromCode = canonicalAndDeprecationsInJson.Except(configurationKeysInCode).ToList();
2703+
2704+
var allKeysInJson = canonicalAndDeprecationsInJson.Concat(aliasesInJson);
2705+
// Find keys that are defined in ConfigurationKeys but missing from JSON
2706+
var missingFromJson = configurationKeysInCode.Except(allKeysInJson).ToList();
2707+
// Report results
2708+
if (missingFromJson.Any())
2709+
{
2710+
Logger.Error("Configuration keys defined in ConfigurationKeys but missing from supported-configurations.json:");
2711+
foreach (var key in missingFromJson.OrderBy(k => k))
2712+
{
2713+
var fieldInfo = keyToFieldMap.GetValueOrDefault(key, "Unknown");
2714+
Logger.Error(" - {Key} (defined as {FieldInfo})", key, fieldInfo);
2715+
}
2716+
}
2717+
2718+
if (missingFromCode.Any())
2719+
{
2720+
Logger.Warning("Configuration keys in supported-configurations.json but not defined in ConfigurationKeys:");
2721+
foreach (var key in missingFromCode.OrderBy(k => k))
2722+
{
2723+
Logger.Warning(" - {Key}", key);
2724+
}
2725+
}
2726+
2727+
if (!missingFromJson.Any() && !missingFromCode.Any())
2728+
{
2729+
Logger.Information("✅ All configuration keys are properly synchronized between ConfigurationKeys and supported-configurations.json");
2730+
Logger.Information("Total unique configuration keys found across all frameworks: {TotalKeys}", configurationKeysInCode.Count);
2731+
}
2732+
else
2733+
{
2734+
var totalIssues = missingFromJson.Count + missingFromCode.Count;
2735+
Logger.Error("❌ Found {TotalIssues} configuration key synchronization issues", totalIssues);
2736+
2737+
if (missingFromJson.Any())
2738+
{
2739+
throw new InvalidOperationException($"Found {missingFromJson.Count} configuration keys defined in ConfigurationKeys but missing from supported-configurations.json. Please add them to the JSON file.");
2740+
}
2741+
}
2742+
});
2743+
25772744
private async Task CheckLogsForErrors(List<Regex> knownPatterns, bool allFilesMustExist, LogLevel minLogLevel, List<(string IgnoreReasonTag, Regex Regex)> reportablePatterns)
25782745
{
25792746
var logDirectory = BuildDataDirectory / "logs";

tracer/build/_build/Build.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ void DeleteReparsePoints(string path)
210210
.DependsOn(CreateRequiredDirectories)
211211
.DependsOn(Restore)
212212
.DependsOn(CompileManagedSrc)
213+
.DependsOn(CheckConfigurationKeysAgainstJsonValidations)
213214
.DependsOn(PublishManagedTracer)
214215
.DependsOn(DownloadLibDdwaf)
215216
.DependsOn(CopyLibDdwaf)

0 commit comments

Comments
 (0)