diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index f28c7a51e3e..3127c132789 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -11,18 +11,6 @@ on: - '**.md' jobs: - sql-test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - version: [net6.0,net7.0] - steps: - - uses: actions/checkout@v3 - - - name: Run sql docker-compose.integration - run: docker-compose --file=test/OpenTelemetry.Instrumentation.SqlClient.Tests/docker-compose.yml --file=build/docker-compose.${{ matrix.version }}.yml --project-directory=. up --exit-code-from=tests --build - w3c-trace-context-test: runs-on: ubuntu-latest strategy: diff --git a/build/Common.nonprod.props b/build/Common.nonprod.props index fc4cb09c5a8..2f550fb8ff0 100644 --- a/build/Common.nonprod.props +++ b/build/Common.nonprod.props @@ -42,6 +42,7 @@ [6.0.0,) [17.4.1] [4.18.3,5.0) + 3.0.0 [6.4.0,7.0) [1.0.0,2.0) [6.4.0] diff --git a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TBinaryProtocol.cs b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TBinaryProtocol.cs index 820f5ee51a5..4a3454aa40f 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TBinaryProtocol.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TBinaryProtocol.cs @@ -192,7 +192,7 @@ public override void WriteDouble(double d) WriteI64(BitConverter.DoubleToInt64Bits(d)); } -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER public override void WriteBinary(ReadOnlySpan bytes) { WriteI32(bytes.Length); diff --git a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TCompactProtocol.cs b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TCompactProtocol.cs index 29155a4eec1..75f62278d35 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TCompactProtocol.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TCompactProtocol.cs @@ -356,7 +356,7 @@ public override void WriteDouble(double d) Transport.Write(PreAllocatedBuffer, 0, 8); } -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER public override void WriteBinary(ReadOnlySpan bytes) { Int32ToVarInt((uint)bytes.Length, ref PreAllocatedVarInt); diff --git a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TProtocol.cs b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TProtocol.cs index fdba44886d7..ae88af98a4c 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TProtocol.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/ApacheThrift/Protocol/TProtocol.cs @@ -143,7 +143,7 @@ protected virtual void Dispose(bool disposing) public virtual void WriteString(string s) { -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER if (s.Length <= 128) { Span buffer = stackalloc byte[256]; @@ -165,7 +165,7 @@ public virtual void WriteString(string s) } } -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER public abstract void WriteBinary(ReadOnlySpan bytes); #endif diff --git a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md index cf675a45c3f..80d91226941 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Enabled performance optimizations for .NET 6.0+ runtimes. + ([#4349](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4349)) + ## 1.5.0-alpha.1 Released 2023-Mar-07 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs index f63d95f3647..c839a771f1e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs @@ -335,6 +335,7 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric) var exponentialHistogramData = metricPoint.GetExponentialHistogramData(); dataPoint.Scale = exponentialHistogramData.Scale; + dataPoint.ZeroCount = (ulong)exponentialHistogramData.ZeroCount; dataPoint.Positive = new OtlpMetrics.ExponentialHistogramDataPoint.Types.Buckets(); dataPoint.Positive.Offset = exponentialHistogramData.PositiveBuckets.Offset; diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index 9b6e8f5c87f..62e3c10d6b7 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -383,6 +383,8 @@ public void TestCounterToOtlpMetric(string name, string description, string unit [Theory] [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] [InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)] @@ -472,6 +474,8 @@ public void TestUpDownCounterToOtlpMetric(string name, string description, strin [Theory] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] [InlineData("test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)] @@ -497,11 +501,13 @@ public void TestExponentialHistogramToOtlpMetric(string name, string description { var histogram = meter.CreateHistogram(name, unit, description); histogram.Record(longValue.Value, attributes); + histogram.Record(0, attributes); } else { var histogram = meter.CreateHistogram(name, unit, description); histogram.Record(doubleValue.Value, attributes); + histogram.Record(0, attributes); } provider.ForceFlush(); @@ -537,23 +543,49 @@ public void TestExponentialHistogramToOtlpMetric(string name, string description Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); - Assert.Equal(1UL, dataPoint.Count); + Assert.Equal(20, dataPoint.Scale); + Assert.Equal(2UL, dataPoint.Count); + Assert.Equal(1UL, dataPoint.ZeroCount); if (longValue.HasValue) { + // Known issue: Negative measurements affect the Sum. Per the spec, they should not. Assert.Equal((double)longValue, dataPoint.Sum); + if (longValue > 0) + { + Assert.True(dataPoint.Positive.Offset > 0); + Assert.Equal(1UL, dataPoint.Positive.BucketCounts[0]); + Assert.True(dataPoint.Negative.Offset == 0); + Assert.Empty(dataPoint.Negative.BucketCounts); + } + else + { + Assert.True(dataPoint.Negative.Offset > 0); + Assert.Equal(1UL, dataPoint.Negative.BucketCounts[0]); + Assert.True(dataPoint.Positive.Offset == 0); + Assert.Empty(dataPoint.Positive.BucketCounts); + } } else { + // Known issue: Negative measurements affect the Sum. Per the spec, they should not. Assert.Equal(doubleValue, dataPoint.Sum); + if (doubleValue > 0) + { + Assert.True(dataPoint.Positive.Offset > 0); + Assert.Equal(1UL, dataPoint.Positive.BucketCounts[0]); + Assert.True(dataPoint.Negative.Offset == 0); + Assert.Empty(dataPoint.Negative.BucketCounts); + } + else + { + Assert.True(dataPoint.Negative.Offset > 0); + Assert.Equal(1UL, dataPoint.Negative.BucketCounts[0]); + Assert.True(dataPoint.Positive.Offset == 0); + Assert.Empty(dataPoint.Positive.BucketCounts); + } } - Assert.Equal(0UL, dataPoint.ZeroCount); - Assert.Equal(20, dataPoint.Scale); - Assert.True(dataPoint.Positive.Offset > 0); - Assert.Equal(1UL, dataPoint.Positive.BucketCounts[0]); - Assert.True(dataPoint.Negative.Offset <= 0); - if (attributes.Length > 0) { OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes); @@ -569,6 +601,8 @@ public void TestExponentialHistogramToOtlpMetric(string name, string description [Theory] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] [InlineData("test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)] @@ -632,6 +666,7 @@ public void TestHistogramToOtlpMetric(string name, string description, string un Assert.Equal(1UL, dataPoint.Count); + // Known issue: Negative measurements affect the Sum. Per the spec, they should not. if (longValue.HasValue) { Assert.Equal((double)longValue, dataPoint.Sum); diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/Dockerfile b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/Dockerfile deleted file mode 100644 index 1083201dec8..00000000000 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -# Create a container for running the OpenTelemetry SQL Client integration tests. -# This should be run from the root of the repo: -# docker build --file test/OpenTelemetry.Instrumentation.SqlClient.Tests/Dockerfile . - -ARG BUILD_SDK_VERSION=7.0 -ARG TEST_SDK_VERSION=7.0 - -FROM mcr.microsoft.com/dotnet/sdk:${BUILD_SDK_VERSION} AS build -ARG PUBLISH_CONFIGURATION=Release -ARG PUBLISH_FRAMEWORK=net7.0 -WORKDIR /repo -COPY . ./ -RUN ls -la /repo -WORKDIR "/repo/test/OpenTelemetry.Instrumentation.SqlClient.Tests" -RUN dotnet publish "OpenTelemetry.Instrumentation.SqlClient.Tests.csproj" -c "${PUBLISH_CONFIGURATION}" -f "${PUBLISH_FRAMEWORK}" -o /drop -p:IntegrationBuild=true -p:TARGET_FRAMEWORK=${PUBLISH_FRAMEWORK} - -FROM mcr.microsoft.com/dotnet/sdk:${TEST_SDK_VERSION} AS final -ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait -RUN chmod +x /wait -WORKDIR /test -COPY --from=build /drop . -ENTRYPOINT ["dotnet", "vstest", "OpenTelemetry.Instrumentation.SqlClient.Tests.dll", "--logger:console;verbosity=detailed"] diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/OpenTelemetry.Instrumentation.SqlClient.Tests.csproj b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/OpenTelemetry.Instrumentation.SqlClient.Tests.csproj index 3d0c9a1acba..67a1af302c9 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/OpenTelemetry.Instrumentation.SqlClient.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/OpenTelemetry.Instrumentation.SqlClient.Tests.csproj @@ -11,6 +11,7 @@ + @@ -21,6 +22,8 @@ + + all diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs new file mode 100644 index 00000000000..45f4408d61f --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -0,0 +1,131 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Data; +using System.Diagnostics; +using System.Runtime.InteropServices; +using DotNet.Testcontainers.Containers; +using Microsoft.Data.SqlClient; +using OpenTelemetry.Tests; +using OpenTelemetry.Trace; +using Testcontainers.MsSql; +using Testcontainers.SqlEdge; +using Xunit; + +namespace OpenTelemetry.Instrumentation.SqlClient.Tests +{ + public sealed class SqlClientIntegrationTests : IAsyncLifetime + { + // The Microsoft SQL Server Docker image is not compatible with ARM devices, such as Macs with Apple Silicon. + private readonly IContainer databaseContainer = Architecture.Arm64.Equals(RuntimeInformation.ProcessArchitecture) ? new SqlEdgeBuilder().Build() : new MsSqlBuilder().Build(); + + public Task InitializeAsync() + { + return this.databaseContainer.StartAsync(); + } + + public Task DisposeAsync() + { + return this.databaseContainer.DisposeAsync().AsTask(); + } + + [Trait("CategoryName", "SqlIntegrationTests")] + [EnabledOnDockerPlatformTheory(EnabledOnDockerPlatformTheoryAttribute.DockerPlatform.Linux)] + [InlineData(CommandType.Text, "select 1/1", false)] + [InlineData(CommandType.Text, "select 1/1", false, true)] + [InlineData(CommandType.Text, "select 1/0", false, false, true)] + [InlineData(CommandType.Text, "select 1/0", false, false, true, false, false)] + [InlineData(CommandType.Text, "select 1/0", false, false, true, true, false)] + [InlineData(CommandType.StoredProcedure, "sp_who", false)] + [InlineData(CommandType.StoredProcedure, "sp_who", true)] + public void SuccessfulCommandTest( + CommandType commandType, + string commandText, + bool captureStoredProcedureCommandName, + bool captureTextCommandContent = false, + bool isFailure = false, + bool recordException = false, + bool shouldEnrich = true) + { +#if NETFRAMEWORK + // Disable things not available on netfx + recordException = false; + shouldEnrich = false; +#endif + + var sampler = new TestSampler(); + var activities = new List(); + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetSampler(sampler) + .AddInMemoryExporter(activities) + .AddSqlClientInstrumentation(options => + { +#if !NETFRAMEWORK + options.SetDbStatementForStoredProcedure = captureStoredProcedureCommandName; + options.SetDbStatementForText = captureTextCommandContent; +#else + options.SetDbStatementForText = captureStoredProcedureCommandName || captureTextCommandContent; +#endif + options.RecordException = recordException; + if (shouldEnrich) + { + options.Enrich = SqlClientTests.ActivityEnrichment; + } + }) + .Build(); + + using SqlConnection sqlConnection = new SqlConnection(this.GetConnectionString()); + + sqlConnection.Open(); + + string dataSource = sqlConnection.DataSource; + + sqlConnection.ChangeDatabase("master"); + + using SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection) + { + CommandType = commandType, + }; + + try + { + sqlCommand.ExecuteNonQuery(); + } + catch + { + } + + Assert.Single(activities); + var activity = activities[0]; + + SqlClientTests.VerifyActivityData(commandType, commandText, captureStoredProcedureCommandName, captureTextCommandContent, isFailure, recordException, shouldEnrich, dataSource, activity); + SqlClientTests.VerifySamplingParameters(sampler.LatestSamplingParameters); + } + + private string GetConnectionString() + { + switch (this.databaseContainer) + { + case SqlEdgeContainer container: + return container.GetConnectionString(); + case MsSqlContainer container: + return container.GetConnectionString(); + default: + throw new InvalidOperationException($"Container type ${this.databaseContainer.GetType().Name} not supported."); + } + } + } +} diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs index b72bcbacf5a..b7124668b22 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientTests.cs @@ -27,19 +27,8 @@ namespace OpenTelemetry.Instrumentation.SqlClient.Tests { public class SqlClientTests : IDisposable { - /* - To run the integration tests, set the OTEL_SQLCONNECTIONSTRING machine-level environment variable to a valid Sql Server connection string. - - To use Docker... - 1) Run: docker run -d --name sql2019 -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Pass@word" -p 5433:1433 mcr.microsoft.com/mssql/server:2019-latest - 2) Set OTEL_SQLCONNECTIONSTRING as: Data Source=127.0.0.1,5433; User ID=sa; Password=Pass@word - */ - - private const string SqlConnectionStringEnvVarName = "OTEL_SQLCONNECTIONSTRING"; private const string TestConnectionString = "Data Source=(localdb)\\MSSQLLocalDB;Database=master"; - private static readonly string SqlConnectionString = SkipUnlessEnvVarFoundTheoryAttribute.GetEnvironmentVariable(SqlConnectionStringEnvVarName); - private readonly FakeSqlClientDiagnosticSource fakeSqlClientDiagnosticSource; public SqlClientTests() @@ -81,79 +70,6 @@ public void SqlClient_NamedOptions() Assert.Equal(1, namedExporterOptionsConfigureOptionsInvocations); } - [Trait("CategoryName", "SqlIntegrationTests")] - [SkipUnlessEnvVarFoundTheory(SqlConnectionStringEnvVarName)] - [InlineData(CommandType.Text, "select 1/1", false)] - [InlineData(CommandType.Text, "select 1/1", false, true)] - [InlineData(CommandType.Text, "select 1/0", false, false, true)] - [InlineData(CommandType.Text, "select 1/0", false, false, true, false, false)] - [InlineData(CommandType.Text, "select 1/0", false, false, true, true, false)] - [InlineData(CommandType.StoredProcedure, "sp_who", false)] - [InlineData(CommandType.StoredProcedure, "sp_who", true)] - public void SuccessfulCommandTest( - CommandType commandType, - string commandText, - bool captureStoredProcedureCommandName, - bool captureTextCommandContent = false, - bool isFailure = false, - bool recordException = false, - bool shouldEnrich = true) - { -#if NETFRAMEWORK - // Disable things not available on netfx - recordException = false; - shouldEnrich = false; -#endif - - var sampler = new TestSampler(); - var activities = new List(); - using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .SetSampler(sampler) - .AddInMemoryExporter(activities) - .AddSqlClientInstrumentation(options => - { -#if !NETFRAMEWORK - options.SetDbStatementForStoredProcedure = captureStoredProcedureCommandName; - options.SetDbStatementForText = captureTextCommandContent; -#else - options.SetDbStatementForText = captureStoredProcedureCommandName || captureTextCommandContent; -#endif - options.RecordException = recordException; - if (shouldEnrich) - { - options.Enrich = ActivityEnrichment; - } - }) - .Build(); - - using SqlConnection sqlConnection = new SqlConnection(SqlConnectionString); - - sqlConnection.Open(); - - string dataSource = sqlConnection.DataSource; - - sqlConnection.ChangeDatabase("master"); - - using SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection) - { - CommandType = commandType, - }; - - try - { - sqlCommand.ExecuteNonQuery(); - } - catch - { - } - - Assert.Single(activities); - var activity = activities[0]; - - VerifyActivityData(commandType, commandText, captureStoredProcedureCommandName, captureTextCommandContent, isFailure, recordException, shouldEnrich, dataSource, activity); - VerifySamplingParameters(sampler.LatestSamplingParameters); - } - // DiagnosticListener-based instrumentation is only available on .NET Core #if !NETFRAMEWORK [Theory] @@ -384,7 +300,7 @@ public void ShouldNotCollectTelemetryAndShouldNotPropagateExceptionWhenFilterThr } #endif - private static void VerifyActivityData( + internal static void VerifyActivityData( CommandType commandType, string commandText, bool captureStoredProcedureCommandName, @@ -464,7 +380,7 @@ private static void VerifyActivityData( Assert.Equal(dataSource, activity.GetTagValue(SemanticConventions.AttributePeerService)); } - private static void VerifySamplingParameters(SamplingParameters samplingParameters) + internal static void VerifySamplingParameters(SamplingParameters samplingParameters) { Assert.NotNull(samplingParameters.Tags); Assert.Contains( @@ -473,7 +389,7 @@ private static void VerifySamplingParameters(SamplingParameters samplingParamete && (string)kvp.Value == SqlActivitySourceHelper.MicrosoftSqlServerDatabaseSystemName); } - private static void ActivityEnrichment(Activity activity, string method, object obj) + internal static void ActivityEnrichment(Activity activity, string method, object obj) { activity.SetTag("enriched", "yes"); diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/docker-compose.yml b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/docker-compose.yml deleted file mode 100644 index 2c4b2b2763a..00000000000 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/docker-compose.yml +++ /dev/null @@ -1,25 +0,0 @@ -# Start a sql container and then run OpenTelemetry sql integration tests. -# This should be run from the root of the repo: -# opentelemetry>docker-compose --file=test/OpenTelemetry.Instrumentation.SqlClient.Tests/docker-compose.yml --project-directory=. up --exit-code-from=tests --build -version: '3.7' - -services: - sql: - image: mcr.microsoft.com/mssql/server:2019-latest - environment: - - ACCEPT_EULA=Y - # Note: This password is for the ephemeral sql instance running in the container used for tests. Nothing that needs to be handled securely. - - SA_PASSWORD=Pass@word18 - ports: - - "1433:1433" - - tests: - build: - context: . - dockerfile: ./test/OpenTelemetry.Instrumentation.SqlClient.Tests/Dockerfile - entrypoint: ["bash", "-c", "/wait && dotnet vstest OpenTelemetry.Instrumentation.SqlClient.Tests.dll --TestCaseFilter:CategoryName=SqlIntegrationTests"] - environment: - - OTEL_SQLCONNECTIONSTRING=Data Source=sql; User ID=sa; Password=Pass@word18 - - WAIT_HOSTS=sql:1433 - depends_on: - - sql diff --git a/test/OpenTelemetry.Tests/Shared/EnabledOnDockerPlatformTheoryAttribute.cs b/test/OpenTelemetry.Tests/Shared/EnabledOnDockerPlatformTheoryAttribute.cs new file mode 100644 index 00000000000..fd99e0c4499 --- /dev/null +++ b/test/OpenTelemetry.Tests/Shared/EnabledOnDockerPlatformTheoryAttribute.cs @@ -0,0 +1,86 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using System.Text; +using Xunit; + +namespace OpenTelemetry.Tests; + +/// +/// This skips tests if the required Docker engine is not available. +/// +internal class EnabledOnDockerPlatformTheoryAttribute : TheoryAttribute +{ + /// + /// Initializes a new instance of the class. + /// + public EnabledOnDockerPlatformTheoryAttribute(DockerPlatform dockerPlatform) + { + const string executable = "docker"; + + var stdout = new StringBuilder(); + var stderr = new StringBuilder(); + + void AppendStdout(object sender, DataReceivedEventArgs e) => stdout.Append(e.Data); + void AppendStderr(object sender, DataReceivedEventArgs e) => stderr.Append(e.Data); + + var processStartInfo = new ProcessStartInfo(); + processStartInfo.FileName = executable; + processStartInfo.Arguments = string.Join(" ", "version", "--format '{{.Server.Os}}'"); + processStartInfo.RedirectStandardOutput = true; + processStartInfo.RedirectStandardError = true; + processStartInfo.UseShellExecute = false; + + var process = new Process(); + process.StartInfo = processStartInfo; + process.OutputDataReceived += AppendStdout; + process.ErrorDataReceived += AppendStderr; + + try + { + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + } + finally + { + process.OutputDataReceived -= AppendStdout; + process.ErrorDataReceived -= AppendStderr; + } + + if (0.Equals(process.ExitCode) && stdout.ToString().Contains(dockerPlatform.ToString().ToLowerInvariant())) + { + return; + } + + this.Skip = $"The Docker {dockerPlatform} engine is not available."; + } + + public enum DockerPlatform + { + /// + /// Docker Linux engine. + /// + Linux, + + /// + /// Docker Windows engine. + /// + Windows, + } +}