opentelemetry-dotnet-instru.../test/integration-tests/IntegrationTests.Helpers/EnvironmentHelper.cs

390 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Xunit.Abstractions;
namespace IntegrationTests.Helpers
{
public class EnvironmentHelper
{
private static readonly Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
private static readonly string RuntimeFrameworkDescription = RuntimeInformation.FrameworkDescription.ToLower();
private static string _nukeOutputLocation;
private readonly ITestOutputHelper _output;
private readonly int _major;
private readonly int _minor;
private readonly string _patch = null;
private readonly string _appNamePrepend;
private readonly string _runtime;
private readonly bool _isCoreClr;
private readonly string _samplesDirectory;
private readonly TargetFrameworkAttribute _targetFramework;
private bool _requiresProfiling;
private string _integrationsFileLocation;
private string _profilerFileLocation;
public EnvironmentHelper(
string sampleName,
Type anchorType,
ITestOutputHelper output,
string samplesDirectory = null,
bool prependSamplesToAppName = true,
bool requiresProfiling = true)
{
SampleName = sampleName;
_samplesDirectory = samplesDirectory ?? Path.Combine("test", "test-applications", "integrations");
_targetFramework = Assembly.GetAssembly(anchorType).GetCustomAttribute<TargetFrameworkAttribute>();
_output = output;
_requiresProfiling = requiresProfiling;
var parts = _targetFramework.FrameworkName.Split(',');
_runtime = parts[0];
_isCoreClr = _runtime.Equals(EnvironmentTools.CoreFramework);
var versionParts = parts[1].Replace("Version=v", string.Empty).Split('.');
_major = int.Parse(versionParts[0]);
_minor = int.Parse(versionParts[1]);
if (versionParts.Length == 3)
{
_patch = versionParts[2];
}
_appNamePrepend = prependSamplesToAppName
? "Samples."
: string.Empty;
}
public bool DebugModeEnabled { get; set; } = true;
public Dictionary<string, string> CustomEnvironmentVariables { get; set; } = new Dictionary<string, string>();
public string SampleName { get; }
public string FullSampleName => $"{_appNamePrepend}{SampleName}";
public static bool IsNet5()
{
return Environment.Version.Major >= 5;
}
public static bool IsCoreClr()
{
return RuntimeFrameworkDescription.Contains("core") || IsNet5();
}
public static void ClearProfilerEnvironmentVariables()
{
var environmentVariables = new[]
{
// .NET Core
"CORECLR_ENABLE_PROFILING",
"CORECLR_PROFILER",
"CORECLR_PROFILER_PATH",
"CORECLR_PROFILER_PATH_32",
"CORECLR_PROFILER_PATH_64",
"DOTNET_STARTUP_HOOKS",
// .NET Framework
"COR_ENABLE_PROFILING",
"COR_PROFILER",
"COR_PROFILER_PATH",
// OpenTelemetry
"OTEL_PROFILER_PROCESSES",
"OTEL_DOTNET_TRACER_HOME",
"OTEL_INTEGRATIONS",
"OTEL_DISABLED_INTEGRATIONS",
"OTEL_DOTNET_TRACER_ADDITIONAL_SOURCES",
"OTEL_PROFILER_EXCLUDE_PROCESSES"
};
foreach (string variable in environmentVariables)
{
Environment.SetEnvironmentVariable(variable, null);
}
}
public static string GetNukeBuildOutput()
{
string nukeOutputPath = Path.Combine(
EnvironmentTools.GetSolutionDirectory(),
"bin",
"tracer-home");
if (Directory.Exists(nukeOutputPath))
{
_nukeOutputLocation = nukeOutputPath;
return _nukeOutputLocation;
}
throw new Exception($"Unable to find Nuke output at: {nukeOutputPath}. Ensure Nuke has run first.");
}
public static bool IsRunningOnCI()
{
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
// Github sets CI environment variable
string env = Environment.GetEnvironmentVariable("CI");
return !string.IsNullOrEmpty(env);
}
public void SetEnvironmentVariables(
int agentPort,
int aspNetCorePort,
StringDictionary environmentVariables,
bool enableStartupHook,
string processToProfile = null)
{
string profilerEnabled = _requiresProfiling ? "1" : "0";
string profilerPath = GetProfilerPath();
if (IsCoreClr())
{
// enableStartupHook should be true by default, and the parameter should only be set
// to false when testing the case that instrumentation should not be available.
if (enableStartupHook)
{
string hookPath = GetStartupHookOutputPath();
environmentVariables["DOTNET_STARTUP_HOOKS"] = hookPath;
}
environmentVariables["CORECLR_ENABLE_PROFILING"] = profilerEnabled;
environmentVariables["CORECLR_PROFILER"] = EnvironmentTools.ProfilerClsId;
environmentVariables["CORECLR_PROFILER_PATH"] = profilerPath;
}
else
{
environmentVariables["COR_ENABLE_PROFILING"] = profilerEnabled;
environmentVariables["COR_PROFILER"] = EnvironmentTools.ProfilerClsId;
environmentVariables["COR_PROFILER_PATH"] = profilerPath;
}
if (DebugModeEnabled)
{
environmentVariables["OTEL_TRACE_DEBUG"] = "1";
environmentVariables["OTEL_TRACE_LOG_DIRECTORY"] = Path.Combine(EnvironmentTools.GetSolutionDirectory(), "build_data", "profiler-logs");
}
if (!string.IsNullOrEmpty(processToProfile))
{
environmentVariables["OTEL_PROFILER_PROCESSES"] = Path.GetFileName(processToProfile);
}
string integrations = GetIntegrationsPath();
environmentVariables["OTEL_DOTNET_TRACER_HOME"] = GetNukeBuildOutput();
environmentVariables["OTEL_INTEGRATIONS"] = integrations;
environmentVariables["OTEL_EXPORTER_ZIPKIN_ENDPOINT"] = $"http://127.0.0.1:{agentPort}";
environmentVariables["OTEL_EXPORTER"] = "zipkin";
// for ASP.NET Core sample apps, set the server's port
environmentVariables["ASPNETCORE_URLS"] = $"http://127.0.0.1:{aspNetCorePort}/";
environmentVariables["OTEL_DOTNET_TRACER_ADDITIONAL_SOURCES"] = "Samples.*";
foreach (var key in CustomEnvironmentVariables.Keys)
{
environmentVariables[key] = CustomEnvironmentVariables[key];
}
}
public string GetProfilerPath()
{
if (_profilerFileLocation != null)
{
return _profilerFileLocation;
}
string extension = EnvironmentTools.GetOS() switch
{
"win" => "dll",
"linux" => "so",
"osx" => "dylib",
_ => throw new PlatformNotSupportedException()
};
string fileName = $"OpenTelemetry.ClrProfiler.Native.{extension}";
string nukeOutput = GetNukeBuildOutput();
string profilerPath = EnvironmentTools.IsWindows()
? Path.Combine(nukeOutput, $"win-{EnvironmentTools.GetPlatform().ToLower()}", fileName)
: Path.Combine(nukeOutput, fileName);
if (File.Exists(profilerPath))
{
_profilerFileLocation = profilerPath;
_output?.WriteLine($"Found profiler at {_profilerFileLocation}.");
return _profilerFileLocation;
}
throw new Exception($"Unable to find profiler at: {profilerPath}");
}
public string GetIntegrationsPath()
{
if (_integrationsFileLocation != null)
{
return _integrationsFileLocation;
}
string fileName = $"integrations.json";
string integrationsPath = Path.Combine(GetNukeBuildOutput(), fileName);
if (File.Exists(integrationsPath))
{
_integrationsFileLocation = integrationsPath;
_output?.WriteLine($"Found integrations at {_profilerFileLocation}.");
return _integrationsFileLocation;
}
throw new Exception($"Unable to find integrations at: {integrationsPath}");
}
public string GetSampleApplicationPath(string packageVersion = "", string framework = "")
{
string extension = "exe";
if (IsCoreClr() || _samplesDirectory.Contains("aspnet"))
{
extension = "dll";
}
var appFileName = $"{FullSampleName}.{extension}";
var sampleAppPath = Path.Combine(GetSampleApplicationOutputDirectory(packageVersion: packageVersion, framework: framework), appFileName);
return sampleAppPath;
}
public string GetTestCommandForSampleApplicationPath(string packageVersion = "", string framework = "")
{
var appFileName = $"{FullSampleName}.dll";
var sampleAppPath = Path.Combine(GetSampleApplicationOutputDirectory(packageVersion: packageVersion, framework: framework), appFileName);
return sampleAppPath;
}
public string GetSampleExecutionSource()
{
string executor;
if (_samplesDirectory.Contains("aspnet"))
{
executor = $"C:\\Program Files{(Environment.Is64BitProcess ? string.Empty : " (x86)")}\\IIS Express\\iisexpress.exe";
}
else if (IsCoreClr())
{
executor = EnvironmentTools.IsWindows() ? "dotnet.exe" : "dotnet";
}
else
{
var appFileName = $"{FullSampleName}.exe";
executor = Path.Combine(GetSampleApplicationOutputDirectory(), appFileName);
if (!File.Exists(executor))
{
throw new Exception($"Unable to find executing assembly at {executor}");
}
}
return executor;
}
public string GetDotNetTest()
{
if (EnvironmentTools.IsWindows())
{
if (!IsCoreClr())
{
string filePattern = @"C:\Program Files (x86)\Microsoft Visual Studio\{0}\{1}\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe";
List<Tuple<string, string>> lstTuple = new List<Tuple<string, string>>
{
Tuple.Create("2019", "Enterprise"),
Tuple.Create("2019", "Professional"),
Tuple.Create("2019", "Community"),
Tuple.Create("2017", "Enterprise"),
Tuple.Create("2017", "Professional"),
Tuple.Create("2017", "Community"),
};
foreach (Tuple<string, string> tuple in lstTuple)
{
var tryPath = string.Format(filePattern, tuple.Item1, tuple.Item2);
if (File.Exists(tryPath))
{
return tryPath;
}
}
}
return "dotnet.exe";
}
return "dotnet";
}
public string GetSampleProjectDirectory()
{
var solutionDirectory = EnvironmentTools.GetSolutionDirectory();
var projectDir = Path.Combine(
solutionDirectory,
_samplesDirectory,
$"{FullSampleName}");
return projectDir;
}
public string GetSampleApplicationOutputDirectory(string packageVersion = "", string framework = "")
{
var targetFramework = string.IsNullOrEmpty(framework) ? GetTargetFramework() : framework;
var binDir = Path.Combine(
GetSampleProjectDirectory(),
"bin");
if (_samplesDirectory.Contains("aspnet"))
{
return Path.Combine(
binDir,
EnvironmentTools.GetBuildConfiguration(),
"app.publish");
}
return Path.Combine(
binDir,
packageVersion,
EnvironmentTools.GetPlatform().ToLowerInvariant(),
EnvironmentTools.GetBuildConfiguration(),
targetFramework);
}
public string GetTargetFramework()
{
if (_isCoreClr)
{
if (_major >= 5)
{
return $"net{_major}.{_minor}";
}
return $"netcoreapp{_major}.{_minor}";
}
return $"net{_major}{_minor}{_patch ?? string.Empty}";
}
private static string GetStartupHookOutputPath()
{
string startupHookOutputPath = Path.Combine(
GetNukeBuildOutput(),
"netcoreapp3.1",
"OpenTelemetry.Instrumentation.StartupHook.dll");
return startupHookOutputPath;
}
}
}