Skip to content

Commit

Permalink
feat: Allow container linking for AWS ECS applications. (#2683)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaffinito authored Sep 18, 2024
1 parent 80cf9ef commit d55567f
Show file tree
Hide file tree
Showing 3 changed files with 423 additions and 75 deletions.
22 changes: 22 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Utilization/EcsVendorModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2020 New Relic, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

using Newtonsoft.Json;

namespace NewRelic.Agent.Core.Utilization
{
public class EcsVendorModel : IVendorModel
{
private readonly string _ecsDockerId;

public string VendorName { get { return "ecs"; } }

[JsonProperty("ecsDockerId", NullValueHandling = NullValueHandling.Ignore)]
public string EcsDockerId { get { return _ecsDockerId; } }

public EcsVendorModel(string ecsDockerId)
{
_ecsDockerId = ecsDockerId;
}
}
}
116 changes: 71 additions & 45 deletions src/Agent/NewRelic/Agent/Core/Utilization/VendorInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ namespace NewRelic.Agent.Core.Utilization
public class VendorInfo
{
private const string ValidateMetadataRegex = @"^[a-zA-Z0-9-_. /]*$";
private const string ContainerIdV1Regex = @".*cpu.*([0-9a-f]{64})";
private const string ContainerIdV2Regex = ".*/docker/containers/([0-9a-f]{64})/.*";
private const string StandardDockerIdRegex = "([0-9a-f]{64})";
private const string ContainerIdV1Regex = @".*cpu.*" + StandardDockerIdRegex;
private const string ContainerIdV2Regex = ".*/docker/containers/" + StandardDockerIdRegex + "/.*";
private const string AwsEcsMetadataV3EnvVar = "ECS_CONTAINER_METADATA_URI";
private const string AwsEcsMetadataV4EnvVar = "ECS_CONTAINER_METADATA_URI_V4";

Expand All @@ -32,7 +33,7 @@ public class VendorInfo
private const string PcfName = @"pcf";
private const string DockerName = @"docker";
private const string KubernetesName = @"kubernetes";
private const string EcsFargateName = @"ecs-fargate";
private const string EcsName = @"ecs";

private readonly string AwsTokenUri = @"http://169.254.169.254/latest/api/token";
private readonly string AwsMetadataUri = @"http://169.254.169.254/latest/dynamic/instance-identity/document";
Expand Down Expand Up @@ -72,15 +73,33 @@ public IDictionary<string, IVendorModel> GetVendors()
var vendorMethods = new List<Func<IVendorModel>>();

if (_configuration.UtilizationDetectAws)
{
vendorMethods.Add(GetAwsVendorInfo);

// Directly add the ECS vendor info if AWS is enabled and not null.
var ecsVendorInfo = GetEcsVendorInfo();
if (ecsVendorInfo != null)
{
vendors.Add(ecsVendorInfo.VendorName, ecsVendorInfo);
}

}
if (_configuration.UtilizationDetectAzure)
{
vendorMethods.Add(GetAzureVendorInfo);
}
if (_configuration.UtilizationDetectGcp)
{
vendorMethods.Add(GetGcpVendorInfo);
}
if (_configuration.UtilizationDetectPcf)
{
vendorMethods.Add(GetPcfVendorInfo);
}
if (_configuration.UtilizationDetectAzureFunction)
{
vendorMethods.Add(GetAzureFunctionVendorInfo);
}

foreach (var vendorMethod in vendorMethods)
{
Expand All @@ -94,8 +113,9 @@ public IDictionary<string, IVendorModel> GetVendors()
}
}

// If Docker info is set to be checked, it must be checked for all vendors.
if (_configuration.UtilizationDetectDocker)
// If Docker info is set to be checked, it must be checked for all vendors even disabled ones.
// If we get AWS ECS info, we don't need to check Docker.
if (_configuration.UtilizationDetectDocker && !vendors.ContainsKey(EcsName))
{
var dockerVendorInfo = GetDockerVendorInfo(new FileReaderWrapper(), IsLinux());
if (dockerVendorInfo != null)
Expand Down Expand Up @@ -322,42 +342,6 @@ public IVendorModel GetDockerVendorInfo(IFileReaderWrapper fileReaderWrapper, bo
}
}

if (vendorModel == null)
{
try
{
var metadataUri = GetProcessEnvironmentVariable(AwsEcsMetadataV4EnvVar);
if (!string.IsNullOrWhiteSpace(metadataUri))
{
vendorModel = TryGetEcsFargateDockerId(metadataUri);
if (vendorModel == null)
Log.Finest($"Found {AwsEcsMetadataV4EnvVar} but failed to parse Docker container id.");
}
}
catch (Exception ex)
{
Log.Finest(ex, $"Failed to parse Docker container id from {AwsEcsMetadataV4EnvVar}.");
}
}

if (vendorModel == null)
{
try
{
var metadataUri = GetProcessEnvironmentVariable(AwsEcsMetadataV3EnvVar);
if (!string.IsNullOrWhiteSpace(metadataUri))
{
vendorModel = TryGetEcsFargateDockerId(metadataUri);
if (vendorModel == null)
Log.Finest($"Found {AwsEcsMetadataV3EnvVar} but failed to parse Docker container id.");
}
}
catch (Exception ex)
{
Log.Finest(ex, $"Failed to parse Docker container id from {AwsEcsMetadataV3EnvVar}.");
}
}

return vendorModel;
}

Expand Down Expand Up @@ -403,13 +387,55 @@ private IVendorModel TryGetDockerCGroupV2(string fileContent)
return id == null ? null : new DockerVendorModel(id);
}

private IVendorModel TryGetEcsFargateDockerId(string metadataUri)
public IVendorModel GetEcsVendorInfo()
{
var responseJson = _vendorHttpApiRequestor.CallVendorApi(new Uri(metadataUri), GetMethod, EcsFargateName);
IVendorModel ecsVendorModel = null;
try
{
var metadataUri = GetProcessEnvironmentVariable(AwsEcsMetadataV4EnvVar);
if (!string.IsNullOrWhiteSpace(metadataUri))
{
ecsVendorModel = TryGetEcsVendorModel(metadataUri);
if (ecsVendorModel == null)
{
Log.Finest($"Found {AwsEcsMetadataV4EnvVar} but failed to parse Docker container id.");
}
}
}
catch (Exception ex)
{
Log.Finest(ex, $"Failed to parse Docker container id from {AwsEcsMetadataV4EnvVar}.");
}

if (ecsVendorModel == null)
{
try
{
var metadataUri = GetProcessEnvironmentVariable(AwsEcsMetadataV3EnvVar);
if (!string.IsNullOrWhiteSpace(metadataUri))
{
ecsVendorModel = TryGetEcsVendorModel(metadataUri);
if (ecsVendorModel == null)
{
Log.Finest($"Found {AwsEcsMetadataV3EnvVar} but failed to parse Docker container id.");
}
}
}
catch (Exception ex)
{
Log.Finest(ex, $"Failed to parse Docker container id from {AwsEcsMetadataV3EnvVar}.");
}
}

return ecsVendorModel;
}

private IVendorModel TryGetEcsVendorModel(string metadataUri)
{
var responseJson = _vendorHttpApiRequestor.CallVendorApi(new Uri(metadataUri), GetMethod, EcsName);
var jObject = JObject.Parse(responseJson);
var idToken = jObject.SelectToken("DockerId");
var id = NormalizeAndValidateMetadata((string)idToken, "DockerId", EcsFargateName);
return id == null ? null : new DockerVendorModel(id);
return idToken == null ? null : new EcsVendorModel((string)idToken);
}

public IVendorModel GetKubernetesInfo()
Expand Down
Loading

0 comments on commit d55567f

Please sign in to comment.