Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
51d690b
Mini refactor
tonyredondo Aug 8, 2022
bf63546
ITR skippeable tests client
tonyredondo Aug 9, 2022
28b9f4e
Changes
tonyredondo Aug 10, 2022
ab4ca03
Skippeable tests dictionary.
tonyredondo Aug 10, 2022
6473d7d
Add xUnit ITR skip logic.
tonyredondo Aug 10, 2022
74585f3
Changes.
tonyredondo Aug 11, 2022
19b98a6
fix rebase
tonyredondo Aug 12, 2022
3d4ff4d
fix cherry-pick
tonyredondo Aug 12, 2022
317238b
Enable #nullable enable
tonyredondo Aug 12, 2022
5c8792a
Nomalize Env and ServiceName
tonyredondo Aug 12, 2022
24f797d
fix the backup folder name
tonyredondo Aug 12, 2022
7482b1d
fix tests
tonyredondo Aug 16, 2022
dfc4554
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 16, 2022
16e6b33
More logs
tonyredondo Aug 16, 2022
56c0f31
Payload missing fix
tonyredondo Aug 18, 2022
f66eb93
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 18, 2022
36f3622
fix tests
tonyredondo Aug 18, 2022
3cbde03
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 18, 2022
888d24c
Fix build in C#11
tonyredondo Aug 19, 2022
5a68dbf
Add NUnit ITR support
tonyredondo Aug 19, 2022
58540cf
xUnit changes.
tonyredondo Aug 19, 2022
245c4ae
Add mechanism to disable the tracer profiler.
tonyredondo Aug 19, 2022
313e518
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 19, 2022
b4a63a3
revert agentlesswriter fix
tonyredondo Aug 20, 2022
e30d041
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 20, 2022
c0a730d
performance improvements.
tonyredondo Aug 22, 2022
900280b
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 22, 2022
0b5599f
Changes
tonyredondo Aug 22, 2022
50fb47a
change
tonyredondo Aug 22, 2022
a715849
fix
tonyredondo Aug 22, 2022
0583372
Improvements
tonyredondo Aug 23, 2022
4165da8
Changes
tonyredondo Aug 23, 2022
f2dc407
Changes
tonyredondo Aug 23, 2022
29c3e27
Merge branch 'master' into tony/itr-api-client
tonyredondo Aug 28, 2022
5a1d483
remove unused usings
tonyredondo Aug 28, 2022
9d5969d
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 5, 2022
614591a
Changes and cleaning.
tonyredondo Sep 5, 2022
2da48e3
Adds _dd.ci.itr tag and http headers for debug.
tonyredondo Sep 5, 2022
8d946e3
Prejit target method.
tonyredondo Sep 5, 2022
8a97587
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 6, 2022
9a65181
Changes
tonyredondo Sep 6, 2022
3b41b35
Adds GetProcessImageName()
tonyredondo Sep 6, 2022
f971af2
Changes and Fixes
tonyredondo Sep 7, 2022
abd4215
change names
tonyredondo Sep 7, 2022
73a8e73
Startup improvements and fixes.
tonyredondo Sep 7, 2022
88b7a6d
Loader improvements.
tonyredondo Sep 7, 2022
9ca9476
changes.
tonyredondo Sep 7, 2022
b4bb7ff
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 12, 2022
99e5d85
Fix IApiResponse
tonyredondo Sep 12, 2022
8456891
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 12, 2022
93aba72
revert
tonyredondo Sep 12, 2022
fb9e0c8
Fix typo
tonyredondo Sep 12, 2022
0815817
Fix MethodAccessException when the Assembly is loaded as partially tr…
tonyredondo Sep 12, 2022
36e680b
Agentless timeout fix.
tonyredondo Sep 13, 2022
32f45bc
Fix Timeout
tonyredondo Sep 13, 2022
7785191
Adds warning to the SetSyncronizationContext method.
tonyredondo Sep 13, 2022
bf59726
Move instance is not null check earlier
tonyredondo Sep 13, 2022
2d31032
Adds check and error message to the LifetimeManager.cs
tonyredondo Sep 13, 2022
e36e77e
Refactor ShouldSkip method
tonyredondo Sep 13, 2022
e330a8d
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 13, 2022
59e0fbf
Change skippable tests endpoint
tonyredondo Sep 13, 2022
0f09d8a
Adding query to Settings API support to enable Code Coverage and Test…
tonyredondo Sep 14, 2022
22c6111
Revert change
tonyredondo Sep 14, 2022
be706b0
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 14, 2022
9a7eae2
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 19, 2022
a99980a
fixes.
tonyredondo Sep 19, 2022
6bc5a57
Addressing comments from review
tonyredondo Sep 19, 2022
56e2ea2
Merge branch 'master' into tony/itr-api-client
tonyredondo Sep 19, 2022
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
16 changes: 15 additions & 1 deletion tracer/src/Datadog.Trace.Tools.Runner/RunHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public static int Execute(ApplicationContext applicationContext, CommandContext
var ciVisibilitySettings = Ci.Configuration.CIVisibilitySettings.FromDefaultSources();
var agentless = ciVisibilitySettings.Agentless;
var apiKey = ciVisibilitySettings.ApiKey;
var applicationKey = ciVisibilitySettings.ApplicationKey;

profilerEnvironmentVariables[Configuration.ConfigurationKeys.CIVisibility.Enabled] = "1";
if (!string.IsNullOrEmpty(ciSettings?.ApiKey))
Expand All @@ -74,7 +75,20 @@ public static int Execute(ApplicationContext applicationContext, CommandContext
return 1;
}

if (ciVisibilitySettings.CodeCoverageEnabled)
var enableCodeCoverage = ciVisibilitySettings.CodeCoverageEnabled == true || ciVisibilitySettings.TestsSkippingEnabled == true;

// If we have api and application key, and the code coverage or the tests skippeable environment variables
// are not set when the intelligent test runner is enabled, we query the settings api to check if it should enable coverage or not.
if (!string.IsNullOrEmpty(apiKey) && !string.IsNullOrEmpty(applicationKey) && ciVisibilitySettings.IntelligentTestRunnerEnabled &&
(ciVisibilitySettings.CodeCoverageEnabled == null || ciVisibilitySettings.TestsSkippingEnabled == null))
{
var itrClient = new Ci.IntelligentTestRunnerClient(Ci.CIEnvironmentValues.Instance.WorkspacePath, ciVisibilitySettings);
// we should skip the framework info because we are interested in the target projects info not the runner one.
var itrSettings = itrClient.GetSettingsAsync(skipFrameworkInfo: true).GetAwaiter().GetResult();
enableCodeCoverage = itrSettings.CodeCoverage == true || itrSettings.TestsSkipping == true;
}

if (enableCodeCoverage)
{
// Check if we are running dotnet process
if (string.Equals(args[0], "dotnet", StringComparison.OrdinalIgnoreCase) ||
Expand Down
5 changes: 4 additions & 1 deletion tracer/src/Datadog.Trace/Agent/IApiResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ public static async Task<T> ReadAsTypeAsync<T>(this IApiResponse apiResponse)
private static async Task<StreamReader> GetStreamReader(IApiResponse apiResponse)
{
var stream = await apiResponse.GetStreamAsync().ConfigureAwait(false);
return new StreamReader(stream, apiResponse.ContentEncoding, detectEncodingFromByteOrderMarks: false, (int)apiResponse.ContentLength, leaveOpen: true);
// Server may not send the content length, in that case we use a default value.
// https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs,25
var length = apiResponse.ContentLength > 0 ? (int)apiResponse.ContentLength : 1024;
return new StreamReader(stream, apiResponse.ContentEncoding, detectEncodingFromByteOrderMarks: false, length, leaveOpen: true);
}
}
}
4 changes: 2 additions & 2 deletions tracer/src/Datadog.Trace/Ci/Agent/CIAgentlessWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace Datadog.Trace.Ci.Agent
* │ │ Event Queue │ │ │ └────────────────────────────┘ │ │
* IEvent │ │ │ │ │ │ │
* ───────┼──►│ ├─────┤►│ ┌────────────────────────────┐ │ │
* │ │ Max: 2500 │ │ │ │ CICodeCoveragePayload Buf. │ │ │
* │ │ Max: 25000 │ │ │ │ CICodeCoveragePayload Buf. │ │ │
* │ │ │ │ │ │ │ │ │
* │ └───────────────┘ │ │ │ CoveragePayload │ │ │ CICodeCoveragePayload Url
* │ │ │ │ ├─┼───┼────────────────────────────►
Expand All @@ -58,7 +58,7 @@ namespace Datadog.Trace.Ci.Agent
internal sealed class CIAgentlessWriter : IEventWriter
{
private const int DefaultBatchInterval = 1000;
private const int DefaultMaxItemsInQueue = 2500;
private const int DefaultMaxItemsInQueue = 25000;

private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor<CIAgentlessWriter>();

Expand Down
189 changes: 157 additions & 32 deletions tracer/src/Datadog.Trace/Ci/CIVisibility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>
#nullable enable

using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Datadog.Trace.Agent;
using Datadog.Trace.Agent.Transports;
using Datadog.Trace.Ci.Configuration;
using Datadog.Trace.ClrProfiler;
using Datadog.Trace.Configuration;
using Datadog.Trace.Logging;
using Datadog.Trace.Pdb;
Expand All @@ -22,7 +25,10 @@ internal class CIVisibility
{
private static readonly CIVisibilitySettings _settings = CIVisibilitySettings.FromDefaultSources();
private static int _firstInitialization = 1;
private static Lazy<bool> _enabledLazy = new Lazy<bool>(() => InternalEnabled(), true);
private static Lazy<bool> _enabledLazy = new(() => InternalEnabled(), true);
private static Task? _skippableTestsTask;
private static Dictionary<string, Dictionary<string, IList<SkippableTest>>>? _skippableTestsBySuiteAndName;

internal static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(CIVisibility));

public static bool Enabled => _enabledLazy.Value;
Expand All @@ -31,7 +37,7 @@ internal class CIVisibility

public static CIVisibilitySettings Settings => _settings;

public static CITracerManager Manager
public static CITracerManager? Manager
{
get
{
Expand Down Expand Up @@ -59,63 +65,97 @@ public static void Initialize()
TracerSettings tracerSettings = _settings.TracerSettings;

// Set the service name if empty
Log.Information("Setting up the service name");
Log.Debug("Setting up the service name");
if (string.IsNullOrEmpty(tracerSettings.ServiceName))
{
// Extract repository name from the git url and use it as a default service name.
tracerSettings.ServiceName = GetServiceNameFromRepository(CIEnvironmentValues.Instance.Repository);
}

// Update and upload git tree metadata.
if (_settings.GitUploadEnabled)
// Intelligent Test Runner
if (_settings.IntelligentTestRunnerEnabled)
{
Log.Information("ITR: Update and uploading git tree metadata and getting skippable tests.");
_skippableTestsTask = GetIntelligentTestRunnerSkippableTestsAsync();
LifetimeManager.Instance.AddAsyncShutdownTask(() => _skippableTestsTask);
}
else if (_settings.GitUploadEnabled)
{
Log.Information("Update and uploading git tree metadata.");
// Update and upload git tree metadata.
Log.Information("ITR: Update and uploading git tree metadata.");
var tskItrUpdate = UploadGitMetadataAsync();
LifetimeManager.Instance.AddAsyncShutdownTask(() => tskItrUpdate);
}

// Initialize Tracer
Log.Information("Initialize Test Tracer instance");
TracerManager.ReplaceGlobalManager(tracerSettings.Build(), new CITracerManagerFactory(_settings));
_ = Tracer.Instance;

// Initialize FrameworkDescription
_ = FrameworkDescription.Instance;

// Initialize CIEnvironment
_ = CIEnvironmentValues.Instance;
}

static async Task UploadGitMetadataAsync()
internal static void FlushSpans()
{
var sContext = SynchronizationContext.Current;
try
{
try
{
var itrClient = new IntelligentTestRunnerClient(CIEnvironmentValues.Instance.WorkspacePath, _settings);
await itrClient.UploadRepositoryChangesAsync().ConfigureAwait(false);
}
catch (Exception ex)
SynchronizationContext.SetSynchronizationContext(null);
if (!InternalFlushAsync().Wait(30_000))
{
Log.Error(ex, "ITR: Error uploading repository git metadata.");
Log.Error("Timeout occurred when flushing spans.");
}
}
finally
{
SynchronizationContext.SetSynchronizationContext(sContext);
}
}

internal static void FlushSpans()
internal static Task<IList<SkippableTest>> GetSkippableTestsFromSuiteAndNameAsync(string suite, string name)
{
try
if (_skippableTestsTask is { } skippableTask)
{
var flushThread = new Thread(InternalFlush);
flushThread.IsBackground = false;
flushThread.Name = "FlushThread";
flushThread.Start();
flushThread.Join();
if (skippableTask.IsCompleted)
{
return Task.FromResult(GetSkippableTestsFromSuiteAndName(suite, name));
}

return SlowGetSkippableTestsFromSuiteAndNameAsync(suite, name);
}
catch (Exception ex)

return Task.FromResult((IList<SkippableTest>)Array.Empty<SkippableTest>());

static async Task<IList<SkippableTest>> SlowGetSkippableTestsFromSuiteAndNameAsync(string suite, string name)
{
Log.Error(ex, "Exception occurred when flushing spans.");
await _skippableTestsTask!.ConfigureAwait(false);
return GetSkippableTestsFromSuiteAndName(suite, name);
}

static void InternalFlush()
static IList<SkippableTest> GetSkippableTestsFromSuiteAndName(string suite, string name)
{
if (!InternalFlushAsync().Wait(30_000))
if (_skippableTestsBySuiteAndName is { } skippeableTestBySuite)
{
Log.Error("Timeout occurred when flushing spans.");
if (skippeableTestBySuite.TryGetValue(suite, out var testsInSuite) &&
testsInSuite.TryGetValue(name, out var tests))
{
return tests;
}
}

return Array.Empty<SkippableTest>();
}
}

internal static bool HasSkippableTests()
{
return _skippableTestsBySuiteAndName?.Count > 0;
}

internal static string GetServiceNameFromRepository(string repository)
{
if (!string.IsNullOrEmpty(repository))
Expand Down Expand Up @@ -147,15 +187,19 @@ internal static string GetServiceNameFromRepository(string repository)

internal static IApiRequestFactory GetRequestFactory(ImmutableTracerSettings settings)
{
IApiRequestFactory factory = null;
TimeSpan agentlessTimeout = TimeSpan.FromSeconds(15);
return GetRequestFactory(settings, TimeSpan.FromSeconds(15));
}

internal static IApiRequestFactory GetRequestFactory(ImmutableTracerSettings settings, TimeSpan timeout)
{
IApiRequestFactory? factory = null;

#if NETCOREAPP
Log.Information("Using {FactoryType} for trace transport.", nameof(HttpClientRequestFactory));
factory = new HttpClientRequestFactory(settings.Exporter.AgentUri, AgentHttpHeaderNames.DefaultHeaders, timeout: agentlessTimeout);
factory = new HttpClientRequestFactory(settings.Exporter.AgentUri, AgentHttpHeaderNames.DefaultHeaders, timeout: timeout);
#else
Log.Information("Using {FactoryType} for trace transport.", nameof(ApiWebRequestFactory));
factory = new ApiWebRequestFactory(settings.Exporter.AgentUri, AgentHttpHeaderNames.DefaultHeaders, timeout: agentlessTimeout);
factory = new ApiWebRequestFactory(settings.Exporter.AgentUri, AgentHttpHeaderNames.DefaultHeaders, timeout: timeout);
#endif

if (!string.IsNullOrWhiteSpace(_settings.ProxyHttps))
Expand All @@ -175,7 +219,7 @@ internal static IApiRequestFactory GetRequestFactory(ImmutableTracerSettings set
return factory;
}

NetworkCredential credential = null;
NetworkCredential? credential = null;
if (!string.IsNullOrWhiteSpace(userName))
{
credential = new NetworkCredential(userName, password);
Expand Down Expand Up @@ -243,7 +287,6 @@ private static bool InternalEnabled()
// Try to autodetect based in the domain name.
var domainName = AppDomain.CurrentDomain.FriendlyName ?? string.Empty;
if (domainName.StartsWith("testhost", StringComparison.Ordinal) ||
domainName.StartsWith("vstest", StringComparison.Ordinal) ||
domainName.StartsWith("xunit", StringComparison.Ordinal) ||
domainName.StartsWith("nunit", StringComparison.Ordinal) ||
domainName.StartsWith("MSBuild", StringComparison.Ordinal))
Expand Down Expand Up @@ -276,5 +319,87 @@ static void PropagateCiVisibilityEnvironmentVariable()
}
}
}

private static async Task UploadGitMetadataAsync()
{
try
{
var itrClient = new IntelligentTestRunnerClient(CIEnvironmentValues.Instance.WorkspacePath, _settings);
await itrClient.UploadRepositoryChangesAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Log.Error(ex, "ITR: Error uploading repository git metadata.");
}
}

private static async Task GetIntelligentTestRunnerSkippableTestsAsync()
{
try
{
var itrClient = new IntelligentTestRunnerClient(CIEnvironmentValues.Instance.WorkspacePath, _settings);

// Upload the git metadata
await itrClient.UploadRepositoryChangesAsync().ConfigureAwait(false);

// If any DD_CIVISIBILITY_CODE_COVERAGE_ENABLED or DD_CIVISIBILITY_TESTSSKIPPING_ENABLED has not been set
// We query the settings api for those
if (_settings.CodeCoverageEnabled == null || _settings.TestsSkippingEnabled == null)
{
var settings = await itrClient.GetSettingsAsync().ConfigureAwait(false);

if (_settings.CodeCoverageEnabled == null && settings.CodeCoverage.HasValue)
{
Log.Information("ITR: Code Coverage has been changed to {value} by settings api.", settings.CodeCoverage.Value);
_settings.SetCodeCoverageEnabled(settings.CodeCoverage.Value);
}

if (_settings.TestsSkippingEnabled == null && settings.TestsSkipping.HasValue)
{
Log.Information("ITR: Tests Skipping has been changed to {value} by settings api.", settings.TestsSkipping.Value);
_settings.SetTestsSkippingEnabled(settings.TestsSkipping.Value);
}
}

// Log code coverage status
Log.Information(_settings.CodeCoverageEnabled == true ? "ITR: Tests code coverage is enabled." : "ITR: Tests code coverage is disabled.");

// If the tests skipping feature is enabled we query the api for the tests we have to skip
if (_settings.TestsSkippingEnabled == true)
{
var skippeableTests = await itrClient.GetSkippableTestsAsync().ConfigureAwait(false);
Log.Information<int>("ITR: SkippableTests = {length}.", skippeableTests.Length);

var skippableTestsBySuiteAndName = new Dictionary<string, Dictionary<string, IList<SkippableTest>>>();
foreach (var item in skippeableTests)
{
if (!skippableTestsBySuiteAndName.TryGetValue(item.Suite, out var suite))
{
suite = new Dictionary<string, IList<SkippableTest>>();
skippableTestsBySuiteAndName[item.Suite] = suite;
}

if (!suite.TryGetValue(item.Name, out var name))
{
name = new List<SkippableTest>();
suite[item.Name] = name;
}

name.Add(item);
}

_skippableTestsBySuiteAndName = skippableTestsBySuiteAndName;
Log.Debug<int>("ITR: SkippableTests dictionary has been built.", skippeableTests.Length);
}
else
{
Log.Information("ITR: Tests skipping is disabled.");
}
}
catch (Exception ex)
{
Log.Error(ex, "ITR: Error getting skippeable tests.");
}
}
}
}
Loading