From 8d91837a537b2e522f4b868cc174e5c00c440ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Fri, 19 Aug 2022 21:03:45 +0200 Subject: [PATCH] [Extensions.Docker] File scoped namespace (#587) Co-authored-by: Utkarsh Umesan Pillai --- .../DockerExtensionsEventSource.cs | 61 ++++--- .../Resources/DockerResourceDetector.cs | 159 +++++++++--------- .../Resources/DockerSemanticConventions.cs | 9 +- .../Utils/EncodingUtils.cs | 27 ++- .../Resources/DockerResourceDetectorTests.cs | 135 ++++++++------- 5 files changed, 193 insertions(+), 198 deletions(-) diff --git a/src/OpenTelemetry.Extensions.Docker/DockerExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Docker/DockerExtensionsEventSource.cs index ab522cea8b..0478b5bdb6 100644 --- a/src/OpenTelemetry.Extensions.Docker/DockerExtensionsEventSource.cs +++ b/src/OpenTelemetry.Extensions.Docker/DockerExtensionsEventSource.cs @@ -19,45 +19,44 @@ using System.Globalization; using System.Threading; -namespace OpenTelemetry.Extensions.Docker +namespace OpenTelemetry.Extensions.Docker; + +[EventSource(Name = "OpenTelemetry-Extensions-Docker")] +internal class DockerExtensionsEventSource : EventSource { - [EventSource(Name = "OpenTelemetry-Extensions-Docker")] - internal class DockerExtensionsEventSource : EventSource - { - public static DockerExtensionsEventSource Log = new DockerExtensionsEventSource(); + public static DockerExtensionsEventSource Log = new DockerExtensionsEventSource(); - [NonEvent] - public void ExtractResourceAttributesException(string format, Exception ex) + [NonEvent] + public void ExtractResourceAttributesException(string format, Exception ex) + { + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) { - if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) - { - this.FailedToExtractResourceAttributes(format, ToInvariantString(ex)); - } + this.FailedToExtractResourceAttributes(format, ToInvariantString(ex)); } + } - [Event(1, Message = "Failed to extract resource attributes in '{0}'.", Level = EventLevel.Error)] - public void FailedToExtractResourceAttributes(string format, string exception) + [Event(1, Message = "Failed to extract resource attributes in '{0}'.", Level = EventLevel.Error)] + public void FailedToExtractResourceAttributes(string format, string exception) + { + this.WriteEvent(1, format, exception); + } + + /// + /// Returns a culture-independent string representation of the given object, + /// appropriate for diagnostics tracing. + /// + private static string ToInvariantString(Exception exception) + { + var originalUICulture = Thread.CurrentThread.CurrentUICulture; + + try { - this.WriteEvent(1, format, exception); + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + return exception.ToString(); } - - /// - /// Returns a culture-independent string representation of the given object, - /// appropriate for diagnostics tracing. - /// - private static string ToInvariantString(Exception exception) + finally { - var originalUICulture = Thread.CurrentThread.CurrentUICulture; - - try - { - Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; - return exception.ToString(); - } - finally - { - Thread.CurrentThread.CurrentUICulture = originalUICulture; - } + Thread.CurrentThread.CurrentUICulture = originalUICulture; } } } diff --git a/src/OpenTelemetry.Extensions.Docker/Resources/DockerResourceDetector.cs b/src/OpenTelemetry.Extensions.Docker/Resources/DockerResourceDetector.cs index bb6ab6a13f..fb6d25f452 100644 --- a/src/OpenTelemetry.Extensions.Docker/Resources/DockerResourceDetector.cs +++ b/src/OpenTelemetry.Extensions.Docker/Resources/DockerResourceDetector.cs @@ -20,112 +20,111 @@ using OpenTelemetry.Extensions.Docker.Utils; using OpenTelemetry.Resources; -namespace OpenTelemetry.Extensions.Docker.Resources +namespace OpenTelemetry.Extensions.Docker.Resources; + +/// +/// Resource detector for application running in Docker environment. +/// +public class DockerResourceDetector : IResourceDetector { + private const string FILEPATH = "/proc/self/cgroup"; + + /// + /// Detects the resource attributes from Docker. + /// + /// Resource with key-value pairs of resource attributes. + public Resource Detect() + { + return this.BuildResource(FILEPATH); + } + /// - /// Resource detector for application running in Docker environment. + /// Builds the resource attributes from Container Id in file path. /// - public class DockerResourceDetector : IResourceDetector + /// File path where container id exists. + /// Returns Resource with list of key-value pairs of container resource attributes if container id exists else empty resource. + internal Resource BuildResource(string path) { - private const string FILEPATH = "/proc/self/cgroup"; + var containerId = this.ExtractContainerId(path); - /// - /// Detects the resource attributes from Docker. - /// - /// Resource with key-value pairs of resource attributes. - public Resource Detect() + if (string.IsNullOrEmpty(containerId)) { - return this.BuildResource(FILEPATH); + return Resource.Empty; } - - /// - /// Builds the resource attributes from Container Id in file path. - /// - /// File path where container id exists. - /// Returns Resource with list of key-value pairs of container resource attributes if container id exists else empty resource. - internal Resource BuildResource(string path) + else { - var containerId = this.ExtractContainerId(path); - - if (string.IsNullOrEmpty(containerId)) - { - return Resource.Empty; - } - else - { - return new Resource(new List>() { new KeyValuePair(DockerSemanticConventions.AttributeContainerID, containerId), }); - } + return new Resource(new List>() { new KeyValuePair(DockerSemanticConventions.AttributeContainerID, containerId), }); } + } - /// - /// Extracts Container Id from path. - /// - /// cgroup path. - /// Container Id, Null if not found or exception being thrown. - private string ExtractContainerId(string path) + /// + /// Extracts Container Id from path. + /// + /// cgroup path. + /// Container Id, Null if not found or exception being thrown. + private string ExtractContainerId(string path) + { + try { - try + if (!File.Exists(path)) { - if (!File.Exists(path)) - { - return null; - } + return null; + } - foreach (string line in File.ReadLines(path)) + foreach (string line in File.ReadLines(path)) + { + string containerId = (!string.IsNullOrEmpty(line)) ? this.GetIdFromLine(line) : null; + if (!string.IsNullOrEmpty(containerId)) { - string containerId = (!string.IsNullOrEmpty(line)) ? this.GetIdFromLine(line) : null; - if (!string.IsNullOrEmpty(containerId)) - { - return containerId; - } + return containerId; } } - catch (Exception ex) - { - DockerExtensionsEventSource.Log.ExtractResourceAttributesException($"{nameof(DockerResourceDetector)} : Failed to extract Container id from path", ex); - } - - return null; } - - /// - /// Gets the Container Id from the line after removing the prefix and suffix. - /// - /// line read from cgroup file. - /// Container Id. - private string GetIdFromLine(string line) + catch (Exception ex) { - // This cgroup output line should have the container id in it - int lastSlashIndex = line.LastIndexOf('/'); - if (lastSlashIndex < 0) - { - return null; - } + DockerExtensionsEventSource.Log.ExtractResourceAttributesException($"{nameof(DockerResourceDetector)} : Failed to extract Container id from path", ex); + } - string lastSection = line.Substring(lastSlashIndex + 1); - int startIndex = lastSection.LastIndexOf('-'); - int endIndex = lastSection.LastIndexOf('.'); + return null; + } - string containerId = this.RemovePrefixAndSuffixIfneeded(lastSection, startIndex, endIndex); + /// + /// Gets the Container Id from the line after removing the prefix and suffix. + /// + /// line read from cgroup file. + /// Container Id. + private string GetIdFromLine(string line) + { + // This cgroup output line should have the container id in it + int lastSlashIndex = line.LastIndexOf('/'); + if (lastSlashIndex < 0) + { + return null; + } - if (string.IsNullOrEmpty(containerId) || !EncodingUtils.IsValidHexString(containerId)) - { - return null; - } + string lastSection = line.Substring(lastSlashIndex + 1); + int startIndex = lastSection.LastIndexOf('-'); + int endIndex = lastSection.LastIndexOf('.'); - return containerId; - } + string containerId = this.RemovePrefixAndSuffixIfneeded(lastSection, startIndex, endIndex); - private string RemovePrefixAndSuffixIfneeded(string input, int startIndex, int endIndex) + if (string.IsNullOrEmpty(containerId) || !EncodingUtils.IsValidHexString(containerId)) { - startIndex = (startIndex == -1) ? 0 : startIndex + 1; + return null; + } - if (endIndex == -1) - { - endIndex = input.Length; - } + return containerId; + } + + private string RemovePrefixAndSuffixIfneeded(string input, int startIndex, int endIndex) + { + startIndex = (startIndex == -1) ? 0 : startIndex + 1; - return input.Substring(startIndex, endIndex - startIndex); + if (endIndex == -1) + { + endIndex = input.Length; } + + return input.Substring(startIndex, endIndex - startIndex); } } diff --git a/src/OpenTelemetry.Extensions.Docker/Resources/DockerSemanticConventions.cs b/src/OpenTelemetry.Extensions.Docker/Resources/DockerSemanticConventions.cs index b41d7404af..0b554a79d0 100644 --- a/src/OpenTelemetry.Extensions.Docker/Resources/DockerSemanticConventions.cs +++ b/src/OpenTelemetry.Extensions.Docker/Resources/DockerSemanticConventions.cs @@ -14,10 +14,9 @@ // limitations under the License. // -namespace OpenTelemetry.Extensions.Docker.Resources +namespace OpenTelemetry.Extensions.Docker.Resources; + +internal static class DockerSemanticConventions { - internal static class DockerSemanticConventions - { - public const string AttributeContainerID = "container.id"; - } + public const string AttributeContainerID = "container.id"; } diff --git a/src/OpenTelemetry.Extensions.Docker/Utils/EncodingUtils.cs b/src/OpenTelemetry.Extensions.Docker/Utils/EncodingUtils.cs index 5963f7ecb2..28bd35bc38 100644 --- a/src/OpenTelemetry.Extensions.Docker/Utils/EncodingUtils.cs +++ b/src/OpenTelemetry.Extensions.Docker/Utils/EncodingUtils.cs @@ -17,21 +17,20 @@ using System.Collections.Generic; using System.Linq; -namespace OpenTelemetry.Extensions.Docker.Utils +namespace OpenTelemetry.Extensions.Docker.Utils; + +internal class EncodingUtils { - internal class EncodingUtils + /// + /// Checks if the string is valid hex. + /// + /// string. + /// true if valid else false. + public static bool IsValidHexString(IEnumerable hexString) { - /// - /// Checks if the string is valid hex. - /// - /// string. - /// true if valid else false. - public static bool IsValidHexString(IEnumerable hexString) - { - return hexString.Select(currentCharacter => - (currentCharacter >= '0' && currentCharacter <= '9') || - (currentCharacter >= 'a' && currentCharacter <= 'f') || - (currentCharacter >= 'A' && currentCharacter <= 'F')).All(isHexCharacter => isHexCharacter); - } + return hexString.Select(currentCharacter => + (currentCharacter >= '0' && currentCharacter <= '9') || + (currentCharacter >= 'a' && currentCharacter <= 'f') || + (currentCharacter >= 'A' && currentCharacter <= 'F')).All(isHexCharacter => isHexCharacter); } } diff --git a/test/OpenTelemetry.Extensions.Docker.Tests/Resources/DockerResourceDetectorTests.cs b/test/OpenTelemetry.Extensions.Docker.Tests/Resources/DockerResourceDetectorTests.cs index bebb93d3e8..004410766e 100644 --- a/test/OpenTelemetry.Extensions.Docker.Tests/Resources/DockerResourceDetectorTests.cs +++ b/test/OpenTelemetry.Extensions.Docker.Tests/Resources/DockerResourceDetectorTests.cs @@ -20,93 +20,92 @@ using OpenTelemetry.Resources; using Xunit; -namespace OpenTelemetry.Extensions.Docker.Tests +namespace OpenTelemetry.Extensions.Docker.Tests; + +public class DockerResourceDetectorTests { - public class DockerResourceDetectorTests - { - // Invalid cgroup line - private const string INVALIDCGROUPLINE = - "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz"; + // Invalid cgroup line + private const string INVALIDCGROUPLINE = + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz"; - // cgroup line with prefix - private const string CGROUPLINEWITHPREFIX = - "13:name=systemd:/podruntime/docker/kubepods/crio-e2cc29debdf85dde404998aa128997a819ff"; + // cgroup line with prefix + private const string CGROUPLINEWITHPREFIX = + "13:name=systemd:/podruntime/docker/kubepods/crio-e2cc29debdf85dde404998aa128997a819ff"; - // Expected Container Id with prefix removed - private const string CONTAINERIDWITHPREFIXREMOVED = "e2cc29debdf85dde404998aa128997a819ff"; + // Expected Container Id with prefix removed + private const string CONTAINERIDWITHPREFIXREMOVED = "e2cc29debdf85dde404998aa128997a819ff"; - // cgroup line with suffix - private const string CGROUPLINEWITHSUFFIX = - "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"; + // cgroup line with suffix + private const string CGROUPLINEWITHSUFFIX = + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"; - // Expected Container Id with suffix removed - private const string CONTAINERIDWITHSUFFIXREMOVED = "ac679f8a8319c8cf7d38e1adf263bc08d23"; + // Expected Container Id with suffix removed + private const string CONTAINERIDWITHSUFFIXREMOVED = "ac679f8a8319c8cf7d38e1adf263bc08d23"; - // cgroup line with prefix and suffix - private const string CGROUPLINEWITHPREFIXandSUFFIX = - "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff"; + // cgroup line with prefix and suffix + private const string CGROUPLINEWITHPREFIXandSUFFIX = + "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff"; - // Expected Container Id with both prefix and suffix removed - private const string CONTAINERIDWITHPREFIXANDSUFFIXREMOVED = "dc679f8a8319c8cf7d38e1adf263bc08d23"; + // Expected Container Id with both prefix and suffix removed + private const string CONTAINERIDWITHPREFIXANDSUFFIXREMOVED = "dc679f8a8319c8cf7d38e1adf263bc08d23"; - // cgroup line with container Id - private const string CGROUPLINE = - "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; + // cgroup line with container Id + private const string CGROUPLINE = + "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; - // Expected Container Id - private const string CONTAINERID = - "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; + // Expected Container Id + private const string CONTAINERID = + "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; + + [Fact] + public void TestValidContainer() + { + var dockerResourceDetector = new DockerResourceDetector(); - [Fact] - public void TestValidContainer() + using (TempFile tempFile = new TempFile()) { - var dockerResourceDetector = new DockerResourceDetector(); - - using (TempFile tempFile = new TempFile()) - { - tempFile.Write(CGROUPLINEWITHPREFIX); - Assert.Equal(CONTAINERIDWITHPREFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); - } - - using (TempFile tempFile = new TempFile()) - { - tempFile.Write(CGROUPLINEWITHSUFFIX); - Assert.Equal(CONTAINERIDWITHSUFFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); - } - - using (TempFile tempFile = new TempFile()) - { - tempFile.Write(CGROUPLINEWITHPREFIXandSUFFIX); - Assert.Equal(CONTAINERIDWITHPREFIXANDSUFFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); - } - - using (TempFile tempFile = new TempFile()) - { - tempFile.Write(CGROUPLINE); - Assert.Equal(CONTAINERID, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); - } + tempFile.Write(CGROUPLINEWITHPREFIX); + Assert.Equal(CONTAINERIDWITHPREFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); } - [Fact] - public void TestInvalidContainer() + using (TempFile tempFile = new TempFile()) { - var dockerResourceDetector = new DockerResourceDetector(); + tempFile.Write(CGROUPLINEWITHSUFFIX); + Assert.Equal(CONTAINERIDWITHSUFFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); + } - // test invalid containerId (non-hex) - using (TempFile tempFile = new TempFile()) - { - tempFile.Write(INVALIDCGROUPLINE); - Assert.Equal(dockerResourceDetector.BuildResource(tempFile.FilePath), Resource.Empty); - } + using (TempFile tempFile = new TempFile()) + { + tempFile.Write(CGROUPLINEWITHPREFIXandSUFFIX); + Assert.Equal(CONTAINERIDWITHPREFIXANDSUFFIXREMOVED, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); + } - // test invalid file - Assert.Equal(dockerResourceDetector.BuildResource(Path.GetTempPath()), Resource.Empty); + using (TempFile tempFile = new TempFile()) + { + tempFile.Write(CGROUPLINE); + Assert.Equal(CONTAINERID, this.GetContainerId(dockerResourceDetector.BuildResource(tempFile.FilePath))); } + } + + [Fact] + public void TestInvalidContainer() + { + var dockerResourceDetector = new DockerResourceDetector(); - private string GetContainerId(Resource resource) + // test invalid containerId (non-hex) + using (TempFile tempFile = new TempFile()) { - var resourceAttributes = resource.Attributes.ToDictionary(x => x.Key, x => x.Value); - return resourceAttributes[DockerSemanticConventions.AttributeContainerID]?.ToString(); + tempFile.Write(INVALIDCGROUPLINE); + Assert.Equal(dockerResourceDetector.BuildResource(tempFile.FilePath), Resource.Empty); } + + // test invalid file + Assert.Equal(dockerResourceDetector.BuildResource(Path.GetTempPath()), Resource.Empty); + } + + private string GetContainerId(Resource resource) + { + var resourceAttributes = resource.Attributes.ToDictionary(x => x.Key, x => x.Value); + return resourceAttributes[DockerSemanticConventions.AttributeContainerID]?.ToString(); } }