Skip to content

Commit

Permalink
Merge branch 'main' into abstract-domain-sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
cataggar committed May 30, 2023
2 parents 8472db2 + 9fab3ed commit a949b84
Show file tree
Hide file tree
Showing 36 changed files with 1,497 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

#if !NETFRAMEWORK
using System;
using System.Collections.Generic;
using System.Net.Http;
Expand Down Expand Up @@ -195,8 +196,8 @@ internal static List<KeyValuePair<string, object>> ExtractMetadataV4ResourceAttr
{
while (!streamReader.EndOfStream)
{
var trimmedLine = streamReader.ReadLine().Trim();
if (trimmedLine.Length > 64)
var trimmedLine = streamReader.ReadLine()?.Trim();
if (trimmedLine?.Length > 64)
{
containerId = trimmedLine.Substring(trimmedLine.Length - 64);
return containerId;
Expand All @@ -212,3 +213,4 @@ internal static bool IsECSProcess()
return Environment.GetEnvironmentVariable(AWSECSMetadataURLKey) != null || Environment.GetEnvironmentVariable(AWSECSMetadataURLV4Key) != null;
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
// </copyright>

#if !NETFRAMEWORK

using System;
using System.Collections.Generic;
using System.Net.Http;
Expand Down Expand Up @@ -85,7 +87,7 @@ internal static List<KeyValuePair<string, object>> ExtractResourceAttributes(str
{
while (!streamReader.EndOfStream)
{
stringBuilder.Append(streamReader.ReadLine().Trim());
stringBuilder.Append(streamReader.ReadLine()?.Trim());
}
}

Expand All @@ -109,8 +111,8 @@ internal static List<KeyValuePair<string, object>> ExtractResourceAttributes(str
{
while (!streamReader.EndOfStream)
{
var trimmedLine = streamReader.ReadLine().Trim();
if (trimmedLine.Length > 64)
var trimmedLine = streamReader.ReadLine()?.Trim();
if (trimmedLine?.Length > 64)
{
return trimmedLine.Substring(trimmedLine.Length - 64);
}
Expand Down Expand Up @@ -165,3 +167,4 @@ private static string GetEKSClusterInfo(string credentials, HttpClientHandler? h
return ResourceDetectorUtils.SendOutRequest(AWSClusterInfoUrl, "GET", new KeyValuePair<string, string>("Authorization", credentials), httpClientHandler).Result;
}
}
#endif
14 changes: 6 additions & 8 deletions src/OpenTelemetry.ResourceDetectors.AWS/Http/Handler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
// </copyright>

#if !NETFRAMEWORK

using System;
using System.Net.Http;

Expand All @@ -25,24 +27,19 @@ internal class Handler
{
try
{
ServerCertificateValidationProvider serverCertificateValidationProvider =
ServerCertificateValidationProvider? serverCertificateValidationProvider =
ServerCertificateValidationProvider.FromCertificateFile(certificateFile);

if (!serverCertificateValidationProvider.IsCertificateLoaded ?? false)
if (serverCertificateValidationProvider == null)
{
AWSResourcesEventSource.Log.FailedToValidateCertificate(nameof(Handler), "Failed to Load the certificate file into trusted collection");
return null;
}

if (serverCertificateValidationProvider.ValidationCallback == null)
{
return null;
}

var clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback =
(sender, x509Certificate2, x509Chain, sslPolicyErrors) =>
serverCertificateValidationProvider.ValidationCallback(null, x509Certificate2, x509Chain, sslPolicyErrors);
serverCertificateValidationProvider.ValidationCallback(sender, x509Certificate2, x509Chain, sslPolicyErrors);
return clientHandler;
}
catch (Exception ex)
Expand All @@ -53,3 +50,4 @@ internal class Handler
return null;
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// limitations under the License.
// </copyright>

#if !NETFRAMEWORK

using System;
using System.IO;
using System.Linq;
Expand All @@ -24,43 +26,30 @@ namespace OpenTelemetry.ResourceDetectors.AWS.Http;

internal class ServerCertificateValidationProvider
{
private static readonly ServerCertificateValidationProvider InvalidProvider = new(null);

private readonly X509Certificate2Collection? trustedCertificates;
private readonly X509Certificate2Collection trustedCertificates;

private ServerCertificateValidationProvider(X509Certificate2Collection? trustedCertificates)
private ServerCertificateValidationProvider(X509Certificate2Collection trustedCertificates)
{
if (trustedCertificates == null)
{
this.trustedCertificates = null;
this.ValidationCallback = null;
this.IsCertificateLoaded = false;
return;
}

this.trustedCertificates = trustedCertificates;
this.ValidationCallback = (sender, cert, chain, errors) =>
this.ValidateCertificate(new X509Certificate2(cert), chain, errors);
this.IsCertificateLoaded = true;
this.ValidationCallback = (_, cert, chain, errors) =>
this.ValidateCertificate(cert != null ? new X509Certificate2(cert) : null, chain, errors);
}

public bool? IsCertificateLoaded { get; }

public RemoteCertificateValidationCallback? ValidationCallback { get; }
public RemoteCertificateValidationCallback ValidationCallback { get; }

public static ServerCertificateValidationProvider FromCertificateFile(string certificateFile)
public static ServerCertificateValidationProvider? FromCertificateFile(string certificateFile)
{
if (!File.Exists(certificateFile))
{
AWSResourcesEventSource.Log.FailedToValidateCertificate(nameof(ServerCertificateValidationProvider), "Certificate File does not exist");
return InvalidProvider;
return null;
}

var trustedCertificates = new X509Certificate2Collection();
if (!LoadCertificateToTrustedCollection(trustedCertificates, certificateFile))
{
AWSResourcesEventSource.Log.FailedToValidateCertificate(nameof(ServerCertificateValidationProvider), "Failed to load certificate in trusted collection");
return InvalidProvider;
return null;
}

return new ServerCertificateValidationProvider(trustedCertificates);
Expand Down Expand Up @@ -90,7 +79,7 @@ private static bool HasCommonCertificate(X509Chain chain, X509Certificate2Collec
{
foreach (var certificate in collection)
{
if (Enumerable.SequenceEqual(chainElement.Certificate.GetPublicKey(), certificate.GetPublicKey()))
if (chainElement.Certificate.GetPublicKey().SequenceEqual(certificate.GetPublicKey()))
{
return true;
}
Expand All @@ -100,7 +89,7 @@ private static bool HasCommonCertificate(X509Chain chain, X509Certificate2Collec
return false;
}

private bool ValidateCertificate(X509Certificate2 cert, X509Chain chain, SslPolicyErrors errors)
private bool ValidateCertificate(X509Certificate2? cert, X509Chain? chain, SslPolicyErrors errors)
{
var isSslPolicyPassed = errors == SslPolicyErrors.None ||
errors == SslPolicyErrors.RemoteCertificateChainErrors;
Expand All @@ -117,6 +106,18 @@ private bool ValidateCertificate(X509Certificate2 cert, X509Chain chain, SslPoli
}
}

if (chain == null)
{
AWSResourcesEventSource.Log.FailedToValidateCertificate(nameof(ServerCertificateValidationProvider), "Failed to validate certificate. Certificate chain is null.");
return false;
}

if (cert == null)
{
AWSResourcesEventSource.Log.FailedToValidateCertificate(nameof(ServerCertificateValidationProvider), "Failed to validate certificate. Certificate is null.");
return false;
}

chain.ChainPolicy.ExtraStore.AddRange(this.trustedCertificates);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

Expand Down Expand Up @@ -149,12 +150,9 @@ private bool ValidateCertificate(X509Certificate2 cert, X509Chain chain, SslPoli
}

var trustCertificates = string.Empty;
if (this.trustedCertificates != null)
foreach (var trustCertificate in this.trustedCertificates)
{
foreach (var trustCertificate in this.trustedCertificates)
{
trustCertificates += " " + trustCertificate.Subject;
}
trustCertificates += " " + trustCertificate.Subject;
}

AWSResourcesEventSource.Log.FailedToValidateCertificate(
Expand All @@ -165,3 +163,4 @@ private bool ValidateCertificate(X509Certificate2 cert, X509Chain chain, SslPoli
return isSslPolicyPassed && isValidChain && isTrusted;
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks>net6.0</TargetFrameworks>
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);$(NetFrameworkMinimumSupportedVersion)</TargetFrameworks>
<Description>OpenTelemetry Extensions - AWS Resource Detectors for ElasticBeanstalk, EC2, ECS, EKS.</Description>
<MinVerTagPrefix>ResourceDetectors.AWS-</MinVerTagPrefix>
Expand All @@ -12,17 +12,9 @@

<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="$(OpenTelemetryCoreLatestVersion)" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == '$(NetFrameworkMinimumSupportedVersion)'">
<Compile Remove="AWSECSResourceDetector.cs" />
<Compile Remove="AWSEKSResourceDetector.cs" />
<Compile Remove="Http\Handler.cs" />
<Compile Remove="Http\ServerCertificateValidationProvider.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == '$(NetFrameworkMinimumSupportedVersion)'">
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>

Expand Down
52 changes: 52 additions & 0 deletions src/OpenTelemetry.Sampler.AWS/AWSXRayRemoteSampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,20 @@ internal AWSXRayRemoteSampler(Resource resource, TimeSpan pollingInterval, strin
// upto 5 seconds of jitter for rule polling
this.RulePollerJitter = TimeSpan.FromMilliseconds(Random.Next(1, 5000));

// upto 100 milliseconds of jitter for target polling
this.TargetPollerJitter = TimeSpan.FromMilliseconds(Random.Next(1, 100));

// execute the first update right away and schedule subsequent update later.
this.RulePollerTimer = new Timer(this.GetAndUpdateRules, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);

// set up the target poller to go off once after the default interval. We will update the timer later.
this.TargetPollerTimer = new Timer(this.GetAndUpdateTargets, null, DefaultTargetInterval, Timeout.InfiniteTimeSpan);
}

internal TimeSpan RulePollerJitter { get; set; }

internal TimeSpan TargetPollerJitter { get; set; }

internal Clock Clock { get; set; }

internal string ClientId { get; set; }
Expand All @@ -67,6 +75,8 @@ internal AWSXRayRemoteSampler(Resource resource, TimeSpan pollingInterval, strin

internal Timer RulePollerTimer { get; set; }

internal Timer TargetPollerTimer { get; set; }

internal TimeSpan PollingInterval { get; set; }

internal Trace.Sampler FallbackSampler { get; set; }
Expand Down Expand Up @@ -137,4 +147,46 @@ private async void GetAndUpdateRules(object? state)
// schedule the next rule poll.
this.RulePollerTimer.Change(this.PollingInterval.Add(this.RulePollerJitter), Timeout.InfiniteTimeSpan);
}

private async void GetAndUpdateTargets(object? state)
{
List<SamplingStatisticsDocument> statistics = this.RulesCache.Snapshot(this.Clock.Now());

GetSamplingTargetsRequest request = new GetSamplingTargetsRequest(statistics);
GetSamplingTargetsResponse? response = await this.Client.GetSamplingTargets(request).ConfigureAwait(false);
if (response != null)
{
Dictionary<string, SamplingTargetDocument> targets = new Dictionary<string, SamplingTargetDocument>();
foreach (SamplingTargetDocument target in response.SamplingTargetDocuments)
{
if (target.RuleName != null)
{
targets[target.RuleName] = target;
}
}

this.RulesCache.UpdateTargets(targets);

if (response.LastRuleModification > 0)
{
DateTime lastRuleModificationTime = this.Clock.ToDateTime(response.LastRuleModification);

if (lastRuleModificationTime > this.RulesCache.GetUpdatedAt())
{
// rules have been updated. fetch the new ones right away.
this.RulePollerTimer.Change(TimeSpan.Zero, Timeout.InfiniteTimeSpan);
}
}
}

// schedule next target poll
DateTime nextTargetFetchTime = this.RulesCache.NextTargetFetchTime();
TimeSpan nextTargetFetchInterval = nextTargetFetchTime.Subtract(this.Clock.Now());
if (nextTargetFetchInterval < TimeSpan.Zero)
{
nextTargetFetchInterval = DefaultTargetInterval;
}

this.TargetPollerTimer.Change(nextTargetFetchInterval.Add(this.TargetPollerJitter), Timeout.InfiniteTimeSpan);
}
}
31 changes: 31 additions & 0 deletions src/OpenTelemetry.Sampler.AWS/AWSXRaySamplerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ namespace OpenTelemetry.Sampler.AWS;
internal class AWSXRaySamplerClient : IDisposable
{
private readonly string getSamplingRulesEndpoint;
private readonly string getSamplingTargetsEndpoint;

private readonly HttpClient httpClient;
private readonly string jsonContentType = "application/json";

public AWSXRaySamplerClient(string host)
{
this.getSamplingRulesEndpoint = host + "/GetSamplingRules";
this.getSamplingTargetsEndpoint = host + "/SamplingTargets";
this.httpClient = new HttpClient();
}

Expand Down Expand Up @@ -74,6 +77,34 @@ public async Task<List<SamplingRule>> GetSamplingRules()
return samplingRules;
}

public async Task<GetSamplingTargetsResponse?> GetSamplingTargets(GetSamplingTargetsRequest getSamplingTargetsRequest)
{
var content = new StringContent(JsonSerializer.Serialize(getSamplingTargetsRequest), Encoding.UTF8, this.jsonContentType);

using var request = new HttpRequestMessage(HttpMethod.Post, this.getSamplingTargetsEndpoint)
{
Content = content,
};

var responseJson = await this.DoRequestAsync(this.getSamplingTargetsEndpoint, request).ConfigureAwait(false);

try
{
GetSamplingTargetsResponse? getSamplingTargetsResponse = JsonSerializer
.Deserialize<GetSamplingTargetsResponse>(responseJson);

return getSamplingTargetsResponse;
}
catch (Exception ex)
{
AWSSamplerEventSource.Log.FailedToDeserializeResponse(
nameof(AWSXRaySamplerClient.GetSamplingTargets),
ex.Message);
}

return null;
}

public void Dispose()
{
this.Dispose(true);
Expand Down
2 changes: 1 addition & 1 deletion src/OpenTelemetry.Sampler.AWS/Clock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static Clock GetDefault()

public abstract DateTime Now();

public abstract long NowInSeconds();
public abstract long NowInMilliSeconds();

public abstract DateTime ToDateTime(double seconds);

Expand Down
Loading

0 comments on commit a949b84

Please sign in to comment.