From df0e7a4addec31fd59c83d64f5ad18f60bdc945c Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 08:55:03 +0000
Subject: [PATCH 01/30] Add Kubernetes support in Container Resource Detector
---
opentelemetry-dotnet-contrib.sln | 1 +
...OpenTelemetry.ResourceDetectors.AWS.csproj | 1 +
.../ContainerExtensionsEventSource.cs | 29 ----
.../ContainerResourceDetector.cs | 152 +++++++++++++++---
.../ContainerResourceEventSource.cs | 59 +++++++
.../Models/K8sContainerStatus.cs | 15 ++
.../Models/K8sPod.cs | 12 ++
.../Models/K8sPodStatus.cs | 13 ++
...lemetry.ResourceDetectors.Container.csproj | 4 +
.../SourceGenerationContext.cs | 24 +++
.../ResourceDetectorUtils.cs | 2 +-
...y.ResourceDetectors.Container.Tests.csproj | 4 -
12 files changed, 256 insertions(+), 60 deletions(-)
delete mode 100644 src/OpenTelemetry.ResourceDetectors.Container/ContainerExtensionsEventSource.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/Models/K8sContainerStatus.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPod.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPodStatus.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/SourceGenerationContext.cs
rename src/{OpenTelemetry.ResourceDetectors.AWS => Shared}/ResourceDetectorUtils.cs (98%)
diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln
index a49632c03c..83965ed134 100644
--- a/opentelemetry-dotnet-contrib.sln
+++ b/opentelemetry-dotnet-contrib.sln
@@ -284,6 +284,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{1FCC8E
src\Shared\PropertyFetcher.AOT.cs = src\Shared\PropertyFetcher.AOT.cs
src\Shared\PropertyFetcher.cs = src\Shared\PropertyFetcher.cs
src\Shared\RedactionHelper.cs = src\Shared\RedactionHelper.cs
+ src\Shared\ResourceDetectorUtils.cs = src\Shared\ResourceDetectorUtils.cs
src\Shared\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs
src\Shared\SemanticConventions.cs = src\Shared\SemanticConventions.cs
src\Shared\ServerCertificateValidationHandler.cs = src\Shared\ServerCertificateValidationHandler.cs
diff --git a/src/OpenTelemetry.ResourceDetectors.AWS/OpenTelemetry.ResourceDetectors.AWS.csproj b/src/OpenTelemetry.ResourceDetectors.AWS/OpenTelemetry.ResourceDetectors.AWS.csproj
index 0cec692434..d518de345b 100644
--- a/src/OpenTelemetry.ResourceDetectors.AWS/OpenTelemetry.ResourceDetectors.AWS.csproj
+++ b/src/OpenTelemetry.ResourceDetectors.AWS/OpenTelemetry.ResourceDetectors.AWS.csproj
@@ -24,6 +24,7 @@
+
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerExtensionsEventSource.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerExtensionsEventSource.cs
deleted file mode 100644
index 2eac9e80b2..0000000000
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerExtensionsEventSource.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-using System;
-using System.Diagnostics.Tracing;
-using OpenTelemetry.Internal;
-
-namespace OpenTelemetry.ResourceDetectors.Container;
-
-[EventSource(Name = "OpenTelemetry-ResourceDetectors-Container")]
-internal class ContainerExtensionsEventSource : EventSource
-{
- public static ContainerExtensionsEventSource Log = new();
-
- [NonEvent]
- public void ExtractResourceAttributesException(string format, Exception ex)
- {
- if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
- {
- this.FailedToExtractResourceAttributes(format, ex.ToInvariantString());
- }
- }
-
- [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);
- }
-}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index ec7d1705d3..ed4c2fd1c9 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -4,7 +4,10 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using System.Text;
using System.Text.RegularExpressions;
+using OpenTelemetry.ResourceDetectors.Container.Models;
using OpenTelemetry.ResourceDetectors.Container.Utils;
using OpenTelemetry.Resources;
@@ -18,6 +21,13 @@ public class ContainerResourceDetector : IResourceDetector
private const string Filepath = "/proc/self/cgroup";
private const string FilepathV2 = "/proc/self/mountinfo";
private const string Hostname = "hostname";
+ private const string K8sServiceHostKey = "KUBERNETES_SERVICE_HOST";
+ private const string K8sServicePortKey = "KUBERNETES_SERVICE_PORT_HTTPS";
+ private const string K8sNamespaceKey = "KUBERNETES_NAMESPACE";
+ private const string K8sHostnameKey = "HOSTNAME";
+ private const string K8sContainerNameKey = "KUBERNETES_CONTAINER_NAME";
+ private const string K8sCertificatePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
+ private const string K8sCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
///
/// CGroup Parse Versions.
@@ -33,6 +43,11 @@ internal enum ParseMode
/// Represents CGroupV2.
///
V2,
+
+ ///
+ /// Represents Kubernetes.
+ ///
+ K8s,
}
///
@@ -41,24 +56,29 @@ internal enum ParseMode
/// Resource with key-value pairs of resource attributes.
public Resource Detect()
{
- var cGroupBuild = this.BuildResource(Filepath, ParseMode.V1);
- if (cGroupBuild == Resource.Empty)
+ var resource = this.BuildResource(Filepath, ParseMode.K8s);
+ if (resource == Resource.Empty)
{
- cGroupBuild = this.BuildResource(FilepathV2, ParseMode.V2);
+ resource = this.BuildResource(Filepath, ParseMode.V1);
}
- return cGroupBuild;
+ if (resource == Resource.Empty)
+ {
+ resource = this.BuildResource(FilepathV2, ParseMode.V2);
+ }
+
+ return resource;
}
///
/// Builds the resource attributes from Container Id in file path.
///
/// File path where container id exists.
- /// CGroup Version of file to parse from.
+ /// CGroup Version of file to parse from.
/// Returns Resource with list of key-value pairs of container resource attributes if container id exists else empty resource.
- internal Resource BuildResource(string path, ParseMode cgroupVersion)
+ internal Resource BuildResource(string path, ParseMode parseMode)
{
- var containerId = this.ExtractContainerId(path, cgroupVersion);
+ var containerId = this.ExtractContainerId(path, parseMode);
if (string.IsNullOrEmpty(containerId))
{
@@ -132,45 +152,125 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
return input.Substring(startIndex, endIndex - startIndex);
}
+ private static string? ExtractK8sContainerId()
+ {
+ try
+ {
+ var host = Environment.GetEnvironmentVariable(K8sServiceHostKey);
+ var port = Environment.GetEnvironmentVariable(K8sServicePortKey);
+ var @namespace = Environment.GetEnvironmentVariable(K8sNamespaceKey);
+ var hostname = Environment.GetEnvironmentVariable(K8sHostnameKey);
+ var containerName = Environment.GetEnvironmentVariable(K8sContainerNameKey);
+ var url = $"https://{host}:{port}/api/v1/namespaces/{@namespace}/pods/{hostname}";
+ var credentials = GetK8sCredentials(K8sCredentialPath);
+ using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
+ var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
+ var pod = DeserializeK8sResponse(response);
+
+ if (pod == null || pod.Status == null || pod.Status.ContainerStatuses == null)
+ {
+ return string.Empty;
+ }
+
+ var container = pod.Status.ContainerStatuses.SingleOrDefault(p => p.Name == containerName);
+ if (container is null)
+ {
+ return string.Empty;
+ }
+
+ // Container's ID is in :// format.
+ var index = container.Id.LastIndexOf('/');
+ return container.Id.Substring(index + 1);
+ }
+ catch (Exception ex)
+ {
+ ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)}: Failed to extract container id", ex);
+ }
+
+ return null;
+
+ static string? GetK8sCredentials(string path)
+ {
+ try
+ {
+ var stringBuilder = new StringBuilder();
+
+ using (var streamReader = ResourceDetectorUtils.GetStreamReader(path))
+ {
+ while (!streamReader.EndOfStream)
+ {
+ stringBuilder.Append(streamReader.ReadLine()?.Trim());
+ }
+ }
+
+ stringBuilder.Insert(0, "Bearer ");
+
+ return stringBuilder.ToString();
+ }
+ catch (Exception ex)
+ {
+ ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)}: Failed to load client token", ex);
+ }
+
+ return null;
+ }
+
+ static K8sPod? DeserializeK8sResponse(string response)
+ {
+#if NET6_0_OR_GREATER
+ return ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.K8sPod);
+#else
+ return ResourceDetectorUtils.DeserializeFromString(response);
+#endif
+ }
+ }
+
///
/// Extracts Container Id from path using the cgroupv1 format.
///
/// cgroup path.
- /// CGroup Version of file to parse from.
- /// Container Id, Null if not found or exception being thrown.
- private string? ExtractContainerId(string path, ParseMode cgroupVersion)
+ /// CGroup Version of file to parse from.
+ /// Container Id, if not found or exception being thrown.
+ private string? ExtractContainerId(string path, ParseMode parseMode)
{
try
{
- if (!File.Exists(path))
+ if (parseMode == ParseMode.K8s)
{
- return null;
+ return ExtractK8sContainerId();
}
-
- foreach (string line in File.ReadLines(path))
+ else
{
- string? containerId = null;
- if (!string.IsNullOrEmpty(line))
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+
+ foreach (string line in File.ReadLines(path))
{
- if (cgroupVersion == ParseMode.V1)
+ string? containerId = null;
+ if (!string.IsNullOrEmpty(line))
{
- containerId = GetIdFromLineV1(line);
+ if (parseMode == ParseMode.V1)
+ {
+ containerId = GetIdFromLineV1(line);
+ }
+ else if (parseMode == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
+ {
+ containerId = GetIdFromLineV2(line);
+ }
}
- else if (cgroupVersion == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
+
+ if (!string.IsNullOrEmpty(containerId))
{
- containerId = GetIdFromLineV2(line);
+ return containerId;
}
}
-
- if (!string.IsNullOrEmpty(containerId))
- {
- return containerId;
- }
}
}
catch (Exception ex)
{
- ContainerExtensionsEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)} : Failed to extract Container id from path", ex);
+ ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)} : Failed to extract Container id from path", ex);
}
return null;
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
new file mode 100644
index 0000000000..cffd2eb5ec
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
@@ -0,0 +1,59 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System;
+using System.Diagnostics.Tracing;
+using OpenTelemetry.Internal;
+
+namespace OpenTelemetry.ResourceDetectors.Container;
+
+[EventSource(Name = "OpenTelemetry-ResourceDetectors-Container")]
+internal class ContainerResourceEventSource : EventSource, IServerCertificateValidationEventSource
+{
+ private const int EventIdFailedToExtractResourceAttributes = 1;
+ private const int EventIdFailedToValidateCertificate = 2;
+ private const int EventIdFailedToCreateHttpHandler = 3;
+ private const int EventIdFailedCertificateFileNotExists = 4;
+ private const int EventIdFailedToLoadCertificateInStorage = 5;
+
+ public static ContainerResourceEventSource Log = new();
+
+ [NonEvent]
+ public void ExtractResourceAttributesException(string format, Exception ex)
+ {
+ if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1)))
+ {
+ this.FailedToExtractResourceAttributes(format, ex.ToInvariantString());
+ }
+ }
+
+ [Event(EventIdFailedToExtractResourceAttributes, Message = "Failed to extract resource attributes in '{0}'.", Level = EventLevel.Error)]
+ public void FailedToExtractResourceAttributes(string format, string exception)
+ {
+ this.WriteEvent(1, format, exception);
+ }
+
+ [Event(EventIdFailedToValidateCertificate, Message = "Failed to validate certificate. Details: '{0}'", Level = EventLevel.Warning)]
+ public void FailedToValidateCertificate(string error)
+ {
+ this.WriteEvent(EventIdFailedToValidateCertificate, error);
+ }
+
+ [Event(EventIdFailedToCreateHttpHandler, Message = "Failed to create HTTP handler. Exception: '{0}'", Level = EventLevel.Warning)]
+ public void FailedToCreateHttpHandler(Exception exception)
+ {
+ this.WriteEvent(EventIdFailedToCreateHttpHandler, exception.ToInvariantString());
+ }
+
+ [Event(EventIdFailedCertificateFileNotExists, Message = "Certificate file does not exist. File: '{0}'", Level = EventLevel.Warning)]
+ public void CertificateFileDoesNotExist(string filename)
+ {
+ this.WriteEvent(EventIdFailedCertificateFileNotExists, filename);
+ }
+
+ [Event(EventIdFailedToLoadCertificateInStorage, Message = "Failed to load certificate in trusted storage. File: '{0}'", Level = EventLevel.Warning)]
+ public void FailedToLoadCertificateInTrustedStorage(string filename)
+ {
+ this.WriteEvent(EventIdFailedToLoadCertificateInStorage, filename);
+ }
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sContainerStatus.cs b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sContainerStatus.cs
new file mode 100644
index 0000000000..eafe063be7
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sContainerStatus.cs
@@ -0,0 +1,15 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Text.Json.Serialization;
+
+namespace OpenTelemetry.ResourceDetectors.Container.Models;
+
+internal sealed class K8sContainerStatus
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = default!;
+
+ [JsonPropertyName("containerID")]
+ public string Id { get; set; } = default!;
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPod.cs b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPod.cs
new file mode 100644
index 0000000000..292f314cb7
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPod.cs
@@ -0,0 +1,12 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Text.Json.Serialization;
+
+namespace OpenTelemetry.ResourceDetectors.Container.Models;
+
+internal sealed class K8sPod
+{
+ [JsonPropertyName("status")]
+ public K8sPodStatus? Status { get; set; }
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPodStatus.cs b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPodStatus.cs
new file mode 100644
index 0000000000..f3afc06795
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/Models/K8sPodStatus.cs
@@ -0,0 +1,13 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace OpenTelemetry.ResourceDetectors.Container.Models;
+
+internal sealed class K8sPodStatus
+{
+ [JsonPropertyName("containerStatuses")]
+ public IReadOnlyList ContainerStatuses { get; set; } = new List();
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/OpenTelemetry.ResourceDetectors.Container.csproj b/src/OpenTelemetry.ResourceDetectors.Container/OpenTelemetry.ResourceDetectors.Container.csproj
index c77f1af5e6..8a57acfe65 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/OpenTelemetry.ResourceDetectors.Container.csproj
+++ b/src/OpenTelemetry.ResourceDetectors.Container/OpenTelemetry.ResourceDetectors.Container.csproj
@@ -11,5 +11,9 @@
+
+
+
+
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/SourceGenerationContext.cs b/src/OpenTelemetry.ResourceDetectors.Container/SourceGenerationContext.cs
new file mode 100644
index 0000000000..fc19df95b6
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/SourceGenerationContext.cs
@@ -0,0 +1,24 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#if NET6_0_OR_GREATER
+using System.Text.Json.Serialization;
+using OpenTelemetry.ResourceDetectors.Container.Models;
+
+namespace OpenTelemetry.ResourceDetectors.Container;
+
+///
+/// "Source Generation" is feature added to System.Text.Json in .NET 6.0.
+/// This is a performance optimization that avoids runtime reflection when performing serialization.
+/// Serialization metadata will be computed at compile-time and included in the assembly.
+/// .
+/// .
+/// .
+///
+[JsonSerializable(typeof(K8sPod))]
+[JsonSerializable(typeof(K8sPodStatus))]
+[JsonSerializable(typeof(K8sContainerStatus))]
+internal sealed partial class SourceGenerationContext : JsonSerializerContext
+{
+}
+#endif
diff --git a/src/OpenTelemetry.ResourceDetectors.AWS/ResourceDetectorUtils.cs b/src/Shared/ResourceDetectorUtils.cs
similarity index 98%
rename from src/OpenTelemetry.ResourceDetectors.AWS/ResourceDetectorUtils.cs
rename to src/Shared/ResourceDetectorUtils.cs
index 331f8d86dd..d476e46f22 100644
--- a/src/OpenTelemetry.ResourceDetectors.AWS/ResourceDetectorUtils.cs
+++ b/src/Shared/ResourceDetectorUtils.cs
@@ -12,7 +12,7 @@
#endif
using System.Threading.Tasks;
-namespace OpenTelemetry.ResourceDetectors.AWS;
+namespace OpenTelemetry.ResourceDetectors;
///
/// Class for resource detector utils.
diff --git a/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj b/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
index 6b11db9474..2bc9efb956 100644
--- a/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
+++ b/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
@@ -11,8 +11,4 @@
-
-
-
-
From 7a966f353246c0ab3800e463560244198dd1ca39 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 09:02:19 +0000
Subject: [PATCH 02/30] Update CHANGELOG.md
---
src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md b/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
index c02fca6bb9..dcd4e556dc 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
@@ -5,6 +5,9 @@
* Update OpenTelemetry SDK version to `1.8.1`.
([#1668](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1668))
+* Add Kubernetes support in Container Resource Detector.
+ ([#1699](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1699))
+
## 1.0.0-beta.7
Released 2024-Apr-05
From c7a08b0bd77b950ab3fbfd4aefc73217d435bd1e Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 09:11:58 +0000
Subject: [PATCH 03/30] Fix warnings
---
.../ContainerResourceDetector.cs | 5 +++++
.../ContainerResourceEventSource.cs | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index ed4c2fd1c9..90b47007b5 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -163,6 +163,11 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
var containerName = Environment.GetEnvironmentVariable(K8sContainerNameKey);
var url = $"https://{host}:{port}/api/v1/namespaces/{@namespace}/pods/{hostname}";
var credentials = GetK8sCredentials(K8sCredentialPath);
+ if (string.IsNullOrEmpty(credentials))
+ {
+ return string.Empty;
+ }
+
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
var pod = DeserializeK8sResponse(response);
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
index cffd2eb5ec..0bc7afb24e 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceEventSource.cs
@@ -10,14 +10,14 @@ namespace OpenTelemetry.ResourceDetectors.Container;
[EventSource(Name = "OpenTelemetry-ResourceDetectors-Container")]
internal class ContainerResourceEventSource : EventSource, IServerCertificateValidationEventSource
{
+ public static ContainerResourceEventSource Log = new();
+
private const int EventIdFailedToExtractResourceAttributes = 1;
private const int EventIdFailedToValidateCertificate = 2;
private const int EventIdFailedToCreateHttpHandler = 3;
private const int EventIdFailedCertificateFileNotExists = 4;
private const int EventIdFailedToLoadCertificateInStorage = 5;
- public static ContainerResourceEventSource Log = new();
-
[NonEvent]
public void ExtractResourceAttributesException(string format, Exception ex)
{
From 0beaa5135ae6d3fb3e5da6c356407b6e70c146f3 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 09:22:34 +0000
Subject: [PATCH 04/30] Return string.Empty if container id is null or empty
---
.../ContainerResourceDetector.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 90b47007b5..3ecee12a24 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -178,7 +178,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
}
var container = pod.Status.ContainerStatuses.SingleOrDefault(p => p.Name == containerName);
- if (container is null)
+ if (container is null || string.IsNullOrEmpty(container.Id))
{
return string.Empty;
}
From 966b860019f18e8894a60ffd3fb1b6f74f0257e5 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 12:03:33 +0000
Subject: [PATCH 05/30] Update CHANGELOG.md
---
src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md b/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
index dcd4e556dc..d7735ac8a3 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/CHANGELOG.md
@@ -5,7 +5,7 @@
* Update OpenTelemetry SDK version to `1.8.1`.
([#1668](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1668))
-* Add Kubernetes support in Container Resource Detector.
+* Add Kubernetes support.
([#1699](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1699))
## 1.0.0-beta.7
From fde450411a3f69f6c355a3eba38ab39b7e1a90cc Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 27 Apr 2024 12:06:25 +0000
Subject: [PATCH 06/30] Remove blank line
---
.../ContainerResourceDetector.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 3ecee12a24..4fdc4c91d0 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -171,7 +171,6 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
var pod = DeserializeK8sResponse(response);
-
if (pod == null || pod.Status == null || pod.Status.ContainerStatuses == null)
{
return string.Empty;
From 2af92bc5557dbbfbd57f15cb55dd2ed2864411c8 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Tue, 30 Apr 2024 19:10:02 +0000
Subject: [PATCH 07/30] Apply suggestions from review
---
.../AWSEKSResourceDetector.cs | 4 +---
.../ContainerResourceDetector.cs | 6 ++----
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.AWS/AWSEKSResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.AWS/AWSEKSResourceDetector.cs
index c8a616f2b7..f9991e0b89 100644
--- a/src/OpenTelemetry.ResourceDetectors.AWS/AWSEKSResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.AWS/AWSEKSResourceDetector.cs
@@ -67,7 +67,7 @@ internal static List> ExtractResourceAttributes(str
{
try
{
- var stringBuilder = new StringBuilder();
+ var stringBuilder = new StringBuilder("Bearer ");
using (var streamReader = ResourceDetectorUtils.GetStreamReader(path))
{
@@ -77,8 +77,6 @@ internal static List> ExtractResourceAttributes(str
}
}
- stringBuilder.Insert(0, "Bearer ");
-
return stringBuilder.ToString();
}
catch (Exception ex)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 4fdc4c91d0..3104bd99df 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -171,7 +171,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
var pod = DeserializeK8sResponse(response);
- if (pod == null || pod.Status == null || pod.Status.ContainerStatuses == null)
+ if (pod?.Status?.ContainerStatuses == null)
{
return string.Empty;
}
@@ -197,7 +197,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
{
try
{
- var stringBuilder = new StringBuilder();
+ var stringBuilder = new StringBuilder("Bearer ");
using (var streamReader = ResourceDetectorUtils.GetStreamReader(path))
{
@@ -207,8 +207,6 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
}
}
- stringBuilder.Insert(0, "Bearer ");
-
return stringBuilder.ToString();
}
catch (Exception ex)
From c1fe35fa4464c261fb74c5881a6087bbb01b10d8 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman2@users.noreply.github.com>
Date: Thu, 23 May 2024 06:26:46 +0000
Subject: [PATCH 08/30] Apply suggestions from review
---
.../ContainerResourceDetector.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 3104bd99df..56d1a35257 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -23,9 +23,10 @@ public class ContainerResourceDetector : IResourceDetector
private const string Hostname = "hostname";
private const string K8sServiceHostKey = "KUBERNETES_SERVICE_HOST";
private const string K8sServicePortKey = "KUBERNETES_SERVICE_PORT_HTTPS";
- private const string K8sNamespaceKey = "KUBERNETES_NAMESPACE";
private const string K8sHostnameKey = "HOSTNAME";
+ private const string K8sPodNameKey = "KUBERNETES_POD_NAME";
private const string K8sContainerNameKey = "KUBERNETES_CONTAINER_NAME";
+ private const string K8sNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
private const string K8sCertificatePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
private const string K8sCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
@@ -158,8 +159,8 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
{
var host = Environment.GetEnvironmentVariable(K8sServiceHostKey);
var port = Environment.GetEnvironmentVariable(K8sServicePortKey);
- var @namespace = Environment.GetEnvironmentVariable(K8sNamespaceKey);
- var hostname = Environment.GetEnvironmentVariable(K8sHostnameKey);
+ var @namespace = File.ReadAllText(K8sNamespacePath);
+ var hostname = Environment.GetEnvironmentVariable(K8sPodNameKey) ?? Environment.GetEnvironmentVariable(K8sHostnameKey);
var containerName = Environment.GetEnvironmentVariable(K8sContainerNameKey);
var url = $"https://{host}:{port}/api/v1/namespaces/{@namespace}/pods/{hostname}";
var credentials = GetK8sCredentials(K8sCredentialPath);
From c59199cc355769d85420bd167d0cd306fcc96799 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman2@users.noreply.github.com>
Date: Thu, 23 May 2024 06:27:34 +0000
Subject: [PATCH 09/30] Update README.md
---
.../README.md | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index 4cbfd046c0..8863e64f02 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -34,6 +34,64 @@ your application is running:
- **ContainerResourceDetector**: container.id.
+## Kubernetes
+When running in a Kubernetes environment, the Container resource detector
+requires permissions to access pod information in order to extract the container ID.
+This is achieved using Kubernetes Role-Based Access Control (RBAC).
+
+You will need to create a Role and RoleBinding to grant the necessary permissions
+to the service account used by your application.
+Below is an example of how to configure these RBAC resources:
+
+```yaml
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: pod-reader-account
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ namespace: default
+ name: pod-reader
+rules:
+- apiGroups: [""]
+ resources: ["pods"]
+ verbs: ["get"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: read-pods
+ namespace: default
+subjects:
+- kind: ServiceAccount
+ name: pod-reader-account
+ namespace: default
+roleRef:
+ kind: Role
+ name: pod-reader
+ apiGroup: rbac.authorization.k8s.io
+---
+apiVersion: v1
+kind: Pod
+metadata:
+ name: container-resolve-demo
+spec:
+ serviceAccountName: pod-reader-account
+ volumes:
+ - name: shared-data
+ emptyDir: {}
+ containers:
+ - name: &container_name my_container_name
+ image: ubuntu:latest
+ command: [ "/bin/bash", "-c", "--" ]
+ args: [ "while true; do sleep 30; done;" ]
+ env:
+ - name: KUBERNETES_CONTAINER_NAME
+ value: *container_name
+```
+
## References
- [OpenTelemetry Project](https://opentelemetry.io/)
From ac24d7f49c65106f74fda83465993d7fda43cce7 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman2@users.noreply.github.com>
Date: Thu, 23 May 2024 06:36:41 +0000
Subject: [PATCH 10/30] Update README.md
---
src/OpenTelemetry.ResourceDetectors.Container/README.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index 8863e64f02..b6fcbc141e 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -36,11 +36,12 @@ your application is running:
## Kubernetes
When running in a Kubernetes environment, the Container resource detector
-requires permissions to access pod information in order to extract the container ID.
+requires permissions to access pod information in order
+to extract the container ID.
This is achieved using Kubernetes Role-Based Access Control (RBAC).
-You will need to create a Role and RoleBinding to grant the necessary permissions
-to the service account used by your application.
+You will need to create a Role and RoleBinding to grant
+the necessary permissions to the service account used by your application.
Below is an example of how to configure these RBAC resources:
```yaml
From d5d67d41aa0a0df7dea9cb1975053c595e2f44aa Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman2@users.noreply.github.com>
Date: Thu, 23 May 2024 06:46:19 +0000
Subject: [PATCH 11/30] Update README.md
---
src/OpenTelemetry.ResourceDetectors.Container/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index b6fcbc141e..9c516070fc 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -35,6 +35,7 @@ your application is running:
- **ContainerResourceDetector**: container.id.
## Kubernetes
+
When running in a Kubernetes environment, the Container resource detector
requires permissions to access pod information in order
to extract the container ID.
From 310975b8b9cc2b7c6fee8d4988efda513f584a18 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 23 May 2024 22:08:39 +0000
Subject: [PATCH 12/30] Add unit test
---
.../ContainerResourceDetector.cs | 79 ++---
.../IK8sMetadataFetcher.cs | 19 ++
.../K8sMetadataFetcher.cs | 70 +++++
.../ParseMode.cs | 25 ++
.../ContainerResourceDetectorTests.cs | 131 ++++++--
...y.ResourceDetectors.Container.Tests.csproj | 8 +-
.../k8s/pod-response.json | 289 ++++++++++++++++++
7 files changed, 545 insertions(+), 76 deletions(-)
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/IK8sMetadataFetcher.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
create mode 100644 src/OpenTelemetry.ResourceDetectors.Container/ParseMode.cs
create mode 100644 test/OpenTelemetry.ResourceDetectors.Container.Tests/k8s/pod-response.json
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 56d1a35257..e657f869e9 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
using OpenTelemetry.ResourceDetectors.Container.Models;
using OpenTelemetry.ResourceDetectors.Container.Utils;
@@ -21,34 +20,25 @@ public class ContainerResourceDetector : IResourceDetector
private const string Filepath = "/proc/self/cgroup";
private const string FilepathV2 = "/proc/self/mountinfo";
private const string Hostname = "hostname";
- private const string K8sServiceHostKey = "KUBERNETES_SERVICE_HOST";
- private const string K8sServicePortKey = "KUBERNETES_SERVICE_PORT_HTTPS";
- private const string K8sHostnameKey = "HOSTNAME";
- private const string K8sPodNameKey = "KUBERNETES_POD_NAME";
- private const string K8sContainerNameKey = "KUBERNETES_CONTAINER_NAME";
- private const string K8sNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
private const string K8sCertificatePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
- private const string K8sCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
+
+ private readonly IK8sMetadataFetcher k8sMetadataFetcher;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ContainerResourceDetector()
+ : this(new K8sMetadataFetcher())
+ {
+ }
///
- /// CGroup Parse Versions.
+ /// Initializes a new instance of the class for testing.
///
- internal enum ParseMode
+ /// The .
+ internal ContainerResourceDetector(IK8sMetadataFetcher k8sMetadataFetcher)
{
- ///
- /// Represents CGroupV1.
- ///
- V1,
-
- ///
- /// Represents CGroupV2.
- ///
- V2,
-
- ///
- /// Represents Kubernetes.
- ///
- K8s,
+ this.k8sMetadataFetcher = k8sMetadataFetcher;
}
///
@@ -153,17 +143,16 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
return input.Substring(startIndex, endIndex - startIndex);
}
- private static string? ExtractK8sContainerId()
+ private string? ExtractK8sContainerId()
{
try
{
- var host = Environment.GetEnvironmentVariable(K8sServiceHostKey);
- var port = Environment.GetEnvironmentVariable(K8sServicePortKey);
- var @namespace = File.ReadAllText(K8sNamespacePath);
- var hostname = Environment.GetEnvironmentVariable(K8sPodNameKey) ?? Environment.GetEnvironmentVariable(K8sHostnameKey);
- var containerName = Environment.GetEnvironmentVariable(K8sContainerNameKey);
- var url = $"https://{host}:{port}/api/v1/namespaces/{@namespace}/pods/{hostname}";
- var credentials = GetK8sCredentials(K8sCredentialPath);
+ var baseUrl = this.k8sMetadataFetcher.GetServiceBaseUrl();
+ var @namespace = this.k8sMetadataFetcher.GetNamespace();
+ var hostname = this.k8sMetadataFetcher.GetPodName() ?? this.k8sMetadataFetcher.GetHostname();
+ var containerName = this.k8sMetadataFetcher.GetContainerName();
+ var url = $"{baseUrl}/api/v1/namespaces/{@namespace}/pods/{hostname}";
+ var credentials = this.k8sMetadataFetcher.GetApiCredential();
if (string.IsNullOrEmpty(credentials))
{
return string.Empty;
@@ -194,30 +183,6 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
return null;
- static string? GetK8sCredentials(string path)
- {
- try
- {
- var stringBuilder = new StringBuilder("Bearer ");
-
- using (var streamReader = ResourceDetectorUtils.GetStreamReader(path))
- {
- while (!streamReader.EndOfStream)
- {
- stringBuilder.Append(streamReader.ReadLine()?.Trim());
- }
- }
-
- return stringBuilder.ToString();
- }
- catch (Exception ex)
- {
- ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)}: Failed to load client token", ex);
- }
-
- return null;
- }
-
static K8sPod? DeserializeK8sResponse(string response)
{
#if NET6_0_OR_GREATER
@@ -240,7 +205,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
{
if (parseMode == ParseMode.K8s)
{
- return ExtractK8sContainerId();
+ return this.ExtractK8sContainerId();
}
else
{
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/IK8sMetadataFetcher.cs b/src/OpenTelemetry.ResourceDetectors.Container/IK8sMetadataFetcher.cs
new file mode 100644
index 0000000000..37943c529c
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/IK8sMetadataFetcher.cs
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+namespace OpenTelemetry.ResourceDetectors.Container;
+
+internal interface IK8sMetadataFetcher
+{
+ string? GetApiCredential();
+
+ string? GetContainerName();
+
+ string? GetHostname();
+
+ string? GetPodName();
+
+ string? GetNamespace();
+
+ string? GetServiceBaseUrl();
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs b/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
new file mode 100644
index 0000000000..f4c4ca6710
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
@@ -0,0 +1,70 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using System;
+using System.IO;
+using System.Text;
+
+namespace OpenTelemetry.ResourceDetectors.Container;
+
+internal sealed class K8sMetadataFetcher : IK8sMetadataFetcher
+{
+ private const string KubernetesServiceHostKey = "KUBERNETES_SERVICE_HOST";
+ private const string KubernetesServicePortKey = "KUBERNETES_SERVICE_PORT_HTTPS";
+ private const string KubernetesHostnameKey = "HOSTNAME";
+ private const string KubernetesPodNameKey = "KUBERNETES_POD_NAME";
+ private const string KubernetesContainerNameKey = "KUBERNETES_CONTAINER_NAME";
+ private const string KubernetesNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
+ private const string KubernetesCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
+
+ public string? GetApiCredential()
+ {
+ try
+ {
+ var stringBuilder = new StringBuilder("Bearer ");
+
+ using (var streamReader = ResourceDetectorUtils.GetStreamReader(KubernetesCredentialPath))
+ {
+ while (!streamReader.EndOfStream)
+ {
+ _ = stringBuilder.Append(streamReader.ReadLine()?.Trim());
+ }
+ }
+
+ return stringBuilder.ToString();
+ }
+ catch (Exception ex)
+ {
+ ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerResourceDetector)}: Failed to load client token", ex);
+ }
+
+ return null;
+ }
+
+ public string? GetContainerName()
+ {
+ return Environment.GetEnvironmentVariable(KubernetesContainerNameKey);
+ }
+
+ public string? GetHostname()
+ {
+ return Environment.GetEnvironmentVariable(KubernetesHostnameKey);
+ }
+
+ public string? GetPodName()
+ {
+ return Environment.GetEnvironmentVariable(KubernetesPodNameKey);
+ }
+
+ public string? GetNamespace()
+ {
+ return File.ReadAllText(KubernetesNamespacePath);
+ }
+
+ public string? GetServiceBaseUrl()
+ {
+ var serviceHost = Environment.GetEnvironmentVariable(KubernetesServiceHostKey);
+ var servicePort = Environment.GetEnvironmentVariable(KubernetesServicePortKey);
+ return $"https://{serviceHost}:{servicePort}";
+ }
+}
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ParseMode.cs b/src/OpenTelemetry.ResourceDetectors.Container/ParseMode.cs
new file mode 100644
index 0000000000..3dcaa93d09
--- /dev/null
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ParseMode.cs
@@ -0,0 +1,25 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+namespace OpenTelemetry.ResourceDetectors.Container;
+
+///
+/// CGroup Parse Versions.
+///
+internal enum ParseMode
+{
+ ///
+ /// Represents CGroupV1.
+ ///
+ V1,
+
+ ///
+ /// Represents CGroupV2.
+ ///
+ V2,
+
+ ///
+ /// Represents Kubernetes.
+ ///
+ K8s,
+}
diff --git a/test/OpenTelemetry.ResourceDetectors.Container.Tests/ContainerResourceDetectorTests.cs b/test/OpenTelemetry.ResourceDetectors.Container.Tests/ContainerResourceDetectorTests.cs
index 6ed321e0b1..a97e629016 100644
--- a/test/OpenTelemetry.ResourceDetectors.Container.Tests/ContainerResourceDetectorTests.cs
+++ b/test/OpenTelemetry.ResourceDetectors.Container.Tests/ContainerResourceDetectorTests.cs
@@ -1,9 +1,16 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Http;
using OpenTelemetry.Resources;
using Xunit;
@@ -17,22 +24,22 @@ public class ContainerResourceDetectorTests
name: "cgroupv1 with prefix",
line: "13:name=systemd:/podruntime/docker/kubepods/crio-e2cc29debdf85dde404998aa128997a819ff",
expectedContainerId: "e2cc29debdf85dde404998aa128997a819ff",
- cgroupVersion: ContainerResourceDetector.ParseMode.V1),
+ cgroupVersion: ParseMode.V1),
new(
name: "cgroupv1 with suffix",
line: "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa",
expectedContainerId: "ac679f8a8319c8cf7d38e1adf263bc08d23",
- cgroupVersion: ContainerResourceDetector.ParseMode.V1),
+ cgroupVersion: ParseMode.V1),
new(
name: "cgroupv1 with prefix and suffix",
line: "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff",
expectedContainerId: "dc679f8a8319c8cf7d38e1adf263bc08d23",
- cgroupVersion: ContainerResourceDetector.ParseMode.V1),
+ cgroupVersion: ParseMode.V1),
new(
name: "cgroupv1 with container Id",
line: "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356",
expectedContainerId: "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356",
- cgroupVersion: ContainerResourceDetector.ParseMode.V1),
+ cgroupVersion: ParseMode.V1),
};
private readonly List testValidCasesV2 = new()
@@ -41,32 +48,32 @@ public class ContainerResourceDetectorTests
name: "cgroupv2 with container Id",
line: "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356/hostname",
expectedContainerId: "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
new(
name: "cgroupv2 with full line",
line: "473 456 254:1 /docker/containers/dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw",
expectedContainerId: "dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
new(
name: "cgroupv2 with minikube containerd mountinfo",
line: "1537 1517 8:1 /var/lib/containerd/io.containerd.grpc.v1.cri/sandboxes/fb5916a02feca96bdeecd8e062df9e5e51d6617c8214b5e1f3ff9320f4402ae6/hostname /etc/hostname rw,relatime - ext4 /dev/sda1 rw",
expectedContainerId: "fb5916a02feca96bdeecd8e062df9e5e51d6617c8214b5e1f3ff9320f4402ae6",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
new(
name: "cgroupv2 with minikube docker mountinfo",
line: "2327 2307 8:1 /var/lib/docker/containers/a1551a1d7e1881d6c18d2c9ec462cab6ad3666825f0adb2098e9d5b198fd7e19/hostname /etc/hostname rw,relatime - ext4 /dev/sda1 rw",
expectedContainerId: "a1551a1d7e1881d6c18d2c9ec462cab6ad3666825f0adb2098e9d5b198fd7e19",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
new(
name: "cgroupv2 with minikube docker mountinfo2",
line: "929 920 254:1 /docker/volumes/minikube/_data/lib/docker/containers/0eaa6718003210b6520f7e82d14b4c8d4743057a958a503626240f8d1900bc33/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw",
expectedContainerId: "0eaa6718003210b6520f7e82d14b4c8d4743057a958a503626240f8d1900bc33",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
new(
name: "cgroupv2 with podman mountinfo",
line: "1096 1088 0:104 /containers/overlay-containers/1a2de27e7157106568f7e081e42a8c14858c02bd9df30d6e352b298178b46809/userdata/hostname /etc/hostname rw,nosuid,nodev,relatime - tmpfs tmpfs rw,size=813800k,nr_inodes=203450,mode=700,uid=1000,gid=1000",
expectedContainerId: "1a2de27e7157106568f7e081e42a8c14858c02bd9df30d6e352b298178b46809",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
};
private readonly List testInvalidCases = new()
@@ -74,11 +81,11 @@ public class ContainerResourceDetectorTests
new(
name: "Invalid cgroupv1 line",
line: "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz",
- cgroupVersion: ContainerResourceDetector.ParseMode.V1),
+ cgroupVersion: ParseMode.V1),
new(
name: "Invalid hex cgroupv2 line (contains a z)",
line: "13:name=systemd:/var/lib/containerd/io.containerd.grpc.v1.cri/sandboxes/fb5916a02feca96bdeecd8e062df9e5e51d6617c8214b5e1f3fz9320f4402ae6/hostname",
- cgroupVersion: ContainerResourceDetector.ParseMode.V2),
+ cgroupVersion: ParseMode.V2),
};
[Fact]
@@ -108,7 +115,7 @@ public void TestInvalidContainer()
using var tempFile = new TempFile();
tempFile.Write(testCase.Line);
Assert.Equal(
- containerResourceDetector.BuildResource(tempFile.FilePath, ContainerResourceDetector.ParseMode.V2),
+ containerResourceDetector.BuildResource(tempFile.FilePath, ParseMode.V2),
Resource.Empty);
}
@@ -118,7 +125,7 @@ public void TestInvalidContainer()
using var tempFile = new TempFile();
tempFile.Write(testCase.Line);
Assert.Equal(
- containerResourceDetector.BuildResource(tempFile.FilePath, ContainerResourceDetector.ParseMode.V1),
+ containerResourceDetector.BuildResource(tempFile.FilePath, ParseMode.V1),
Resource.Empty);
}
@@ -131,8 +138,19 @@ public void TestInvalidContainer()
}
// test invalid file
- Assert.Equal(containerResourceDetector.BuildResource(Path.GetTempPath(), ContainerResourceDetector.ParseMode.V1), Resource.Empty);
- Assert.Equal(containerResourceDetector.BuildResource(Path.GetTempPath(), ContainerResourceDetector.ParseMode.V2), Resource.Empty);
+ Assert.Equal(containerResourceDetector.BuildResource(Path.GetTempPath(), ParseMode.V1), Resource.Empty);
+ Assert.Equal(containerResourceDetector.BuildResource(Path.GetTempPath(), ParseMode.V2), Resource.Empty);
+ }
+
+ [Fact]
+ public async Task TestK8sContainerId()
+ {
+ await using (_ = new MockK8sEndpoint("k8s/pod-response.json"))
+ {
+ var resourceAttributes = new ContainerResourceDetector(new MockK8sMetadataFetcher()).Detect().Attributes.ToDictionary(x => x.Key, x => x.Value);
+
+ Assert.Equal(resourceAttributes[ContainerSemanticConventions.AttributeContainerId], "96724c05fa1be8d313f6db0e9872ca542b076839c4fd51ea4912a670ef538cbd");
+ }
}
private static string GetContainerId(Resource resource)
@@ -143,7 +161,7 @@ private static string GetContainerId(Resource resource)
private sealed class TestCase
{
- public TestCase(string name, string line, ContainerResourceDetector.ParseMode cgroupVersion, string? expectedContainerId = null)
+ public TestCase(string name, string line, ParseMode cgroupVersion, string? expectedContainerId = null)
{
this.Name = name;
this.Line = line;
@@ -157,6 +175,83 @@ public TestCase(string name, string line, ContainerResourceDetector.ParseMode cg
public string? ExpectedContainerId { get; }
- public ContainerResourceDetector.ParseMode CgroupVersion { get; }
+ public ParseMode CgroupVersion { get; }
+ }
+
+ private class MockK8sEndpoint : IAsyncDisposable
+ {
+ public readonly Uri Address;
+ private readonly IWebHost server;
+
+ public MockK8sEndpoint(string responseJsonPath)
+ {
+ this.server = new WebHostBuilder()
+ .UseKestrel()
+ .UseUrls("http://127.0.0.1:5000")
+ .Configure(app =>
+ {
+ app.Run(async context =>
+ {
+ if (context.Request.Method == HttpMethods.Get && context.Request.Path == "/api/v1/namespaces/default/pods/pod1")
+ {
+ var content = await File.ReadAllTextAsync($"{Environment.CurrentDirectory}/{responseJsonPath}");
+ var data = Encoding.UTF8.GetBytes(content);
+ context.Response.ContentType = "application/json";
+ await context.Response.Body.WriteAsync(data);
+ }
+ else
+ {
+ context.Response.StatusCode = StatusCodes.Status404NotFound;
+ await context.Response.WriteAsync("Not found");
+ }
+ });
+ }).Build();
+ this.server.Start();
+
+ this.Address = new Uri(this.server.ServerFeatures.Get()!.Addresses.First());
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await this.DisposeAsyncCore();
+ }
+
+ protected virtual async ValueTask DisposeAsyncCore()
+ {
+ await this.server.StopAsync();
+ }
+ }
+
+ private sealed class MockK8sMetadataFetcher : IK8sMetadataFetcher
+ {
+ public string? GetApiCredential()
+ {
+ return Guid.NewGuid().ToString();
+ }
+
+ public string? GetContainerName()
+ {
+ return "my-container";
+ }
+
+ public string? GetHostname()
+ {
+ return "hostname";
+ }
+
+ public string? GetNamespace()
+ {
+ return "default";
+ }
+
+ public string? GetPodName()
+ {
+ return "pod1";
+ }
+
+ public string? GetServiceBaseUrl()
+ {
+ return "http://127.0.0.1:5000";
+ }
}
}
diff --git a/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj b/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
index 2bc9efb956..2c38f24e76 100644
--- a/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
+++ b/test/OpenTelemetry.ResourceDetectors.Container.Tests/OpenTelemetry.ResourceDetectors.Container.Tests.csproj
@@ -7,8 +7,14 @@
enable
+
+
+
+ PreserveNewest
+
+
+
-
diff --git a/test/OpenTelemetry.ResourceDetectors.Container.Tests/k8s/pod-response.json b/test/OpenTelemetry.ResourceDetectors.Container.Tests/k8s/pod-response.json
new file mode 100644
index 0000000000..b82a3e228a
--- /dev/null
+++ b/test/OpenTelemetry.ResourceDetectors.Container.Tests/k8s/pod-response.json
@@ -0,0 +1,289 @@
+{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": {
+ "name": "my-deployment-8d7c572790-46mgg",
+ "generateName": "my-deployment-8d7c572790-",
+ "namespace": "default",
+ "uid": "e393dfee-0ac3-4dd9-a10d-e748c0e3589a",
+ "resourceVersion": "14782",
+ "creationTimestamp": "2024-05-23T09:30:45Z",
+ "labels": {
+ "app": "my-app",
+ "pod-template-hash": "8d7c572790"
+ },
+ "ownerReferences": [
+ {
+ "apiVersion": "apps/v1",
+ "kind": "ReplicaSet",
+ "name": "my-deployment-8d7c572790",
+ "uid": "4aebb45f-c8b0-4406-8b81-3cfc9619d147",
+ "controller": true,
+ "blockOwnerDeletion": true
+ }
+ ],
+ "managedFields": [
+ {
+ "manager": "kube-controller-manager",
+ "operation": "Update",
+ "apiVersion": "v1",
+ "time": "2024-05-23T09:30:45Z",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:metadata": {
+ "f:generateName": {},
+ "f:labels": {
+ ".": {},
+ "f:app": {},
+ "f:pod-template-hash": {}
+ },
+ "f:ownerReferences": {
+ ".": {},
+ "k:{\"uid\":\"4aebb45f-c8b0-4406-8b81-3cfc9619d147\"}": {}
+ }
+ },
+ "f:spec": {
+ "f:containers": {
+ "k:{\"name\":\"my-container\"}": {
+ ".": {},
+ "f:env": {
+ ".": {},
+ "k:{\"name\":\"KUBERNETES_CONTAINER_NAME\"}": {
+ ".": {},
+ "f:name": {},
+ "f:value": {}
+ }
+ },
+ "f:image": {},
+ "f:imagePullPolicy": {},
+ "f:name": {},
+ "f:resources": {},
+ "f:terminationMessagePath": {},
+ "f:terminationMessagePolicy": {}
+ }
+ },
+ "f:dnsPolicy": {},
+ "f:enableServiceLinks": {},
+ "f:restartPolicy": {},
+ "f:schedulerName": {},
+ "f:securityContext": {},
+ "f:serviceAccount": {},
+ "f:serviceAccountName": {},
+ "f:terminationGracePeriodSeconds": {}
+ }
+ }
+ },
+ {
+ "manager": "kubelet",
+ "operation": "Update",
+ "apiVersion": "v1",
+ "time": "2024-05-23T09:30:47Z",
+ "fieldsType": "FieldsV1",
+ "fieldsV1": {
+ "f:status": {
+ "f:conditions": {
+ "k:{\"type\":\"ContainersReady\"}": {
+ ".": {},
+ "f:lastProbeTime": {},
+ "f:lastTransitionTime": {},
+ "f:status": {},
+ "f:type": {}
+ },
+ "k:{\"type\":\"Initialized\"}": {
+ ".": {},
+ "f:lastProbeTime": {},
+ "f:lastTransitionTime": {},
+ "f:status": {},
+ "f:type": {}
+ },
+ "k:{\"type\":\"PodReadyToStartContainers\"}": {
+ ".": {},
+ "f:lastProbeTime": {},
+ "f:lastTransitionTime": {},
+ "f:status": {},
+ "f:type": {}
+ },
+ "k:{\"type\":\"Ready\"}": {
+ ".": {},
+ "f:lastProbeTime": {},
+ "f:lastTransitionTime": {},
+ "f:status": {},
+ "f:type": {}
+ }
+ },
+ "f:containerStatuses": {},
+ "f:hostIP": {},
+ "f:hostIPs": {},
+ "f:phase": {},
+ "f:podIP": {},
+ "f:podIPs": {
+ ".": {},
+ "k:{\"ip\":\"10.244.0.11\"}": {
+ ".": {},
+ "f:ip": {}
+ }
+ },
+ "f:startTime": {}
+ }
+ },
+ "subresource": "status"
+ }
+ ]
+ },
+ "spec": {
+ "volumes": [
+ {
+ "name": "kube-api-access-npqxx",
+ "projected": {
+ "sources": [
+ {
+ "serviceAccountToken": {
+ "expirationSeconds": 3607,
+ "path": "token"
+ }
+ },
+ {
+ "configMap": {
+ "name": "kube-root-ca.crt",
+ "items": [
+ {
+ "key": "ca.crt",
+ "path": "ca.crt"
+ }
+ ]
+ }
+ },
+ {
+ "downwardAPI": {
+ "items": [
+ {
+ "path": "namespace",
+ "fieldRef": {
+ "apiVersion": "v1",
+ "fieldPath": "metadata.namespace"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "defaultMode": 420
+ }
+ }
+ ],
+ "containers": [
+ {
+ "name": "my-container",
+ "image": "app:latest",
+ "env": [
+ {
+ "name": "KUBERNETES_CONTAINER_NAME",
+ "value": "my-container"
+ }
+ ],
+ "resources": {},
+ "volumeMounts": [
+ {
+ "name": "kube-api-access-npqxx",
+ "readOnly": true,
+ "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
+ }
+ ],
+ "terminationMessagePath": "/dev/termination-log",
+ "terminationMessagePolicy": "File",
+ "imagePullPolicy": "Never"
+ }
+ ],
+ "restartPolicy": "Always",
+ "terminationGracePeriodSeconds": 30,
+ "dnsPolicy": "ClusterFirst",
+ "serviceAccountName": "my-service-account",
+ "serviceAccount": "my-service-account",
+ "nodeName": "minikube",
+ "securityContext": {},
+ "schedulerName": "default-scheduler",
+ "tolerations": [
+ {
+ "key": "node.kubernetes.io/not-ready",
+ "operator": "Exists",
+ "effect": "NoExecute",
+ "tolerationSeconds": 300
+ },
+ {
+ "key": "node.kubernetes.io/unreachable",
+ "operator": "Exists",
+ "effect": "NoExecute",
+ "tolerationSeconds": 300
+ }
+ ],
+ "priority": 0,
+ "enableServiceLinks": true,
+ "preemptionPolicy": "PreemptLowerPriority"
+ },
+ "status": {
+ "phase": "Running",
+ "conditions": [
+ {
+ "type": "PodReadyToStartContainers",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2024-05-23T09:30:47Z"
+ },
+ {
+ "type": "Initialized",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2024-05-23T09:30:45Z"
+ },
+ {
+ "type": "Ready",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2024-05-23T09:30:47Z"
+ },
+ {
+ "type": "ContainersReady",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2024-05-23T09:30:47Z"
+ },
+ {
+ "type": "PodScheduled",
+ "status": "True",
+ "lastProbeTime": null,
+ "lastTransitionTime": "2024-05-23T09:30:45Z"
+ }
+ ],
+ "hostIP": "192.168.49.1",
+ "hostIPs": [
+ {
+ "ip": "192.168.49.1"
+ }
+ ],
+ "podIP": "10.244.0.11",
+ "podIPs": [
+ {
+ "ip": "10.244.0.11"
+ }
+ ],
+ "startTime": "2024-05-23T09:30:45Z",
+ "containerStatuses": [
+ {
+ "name": "my-container",
+ "state": {
+ "running": {
+ "startedAt": "2024-05-23T09:30:46Z"
+ }
+ },
+ "lastState": {},
+ "ready": true,
+ "restartCount": 0,
+ "image": "app:latest",
+ "imageID": "docker://sha256:7f8ec1d205a9364bf760b398cd21ae547ea08fd6213b594c64da80735bcf1e29",
+ "containerID": "docker://96724c05fa1be8d313f6db0e9872ca542b076839c4fd51ea4912a670ef538cbd",
+ "started": true
+ }
+ ],
+ "qosClass": "BestEffort"
+ }
+}
From d609b3918bbfb567074a2fae53cf649f6bd636e5 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 23 May 2024 23:17:11 +0000
Subject: [PATCH 13/30] Add null check
---
.../K8sMetadataFetcher.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs b/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
index f4c4ca6710..4ab5666d8f 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/K8sMetadataFetcher.cs
@@ -65,6 +65,12 @@ internal sealed class K8sMetadataFetcher : IK8sMetadataFetcher
{
var serviceHost = Environment.GetEnvironmentVariable(KubernetesServiceHostKey);
var servicePort = Environment.GetEnvironmentVariable(KubernetesServicePortKey);
+
+ if (string.IsNullOrWhiteSpace(serviceHost) || string.IsNullOrWhiteSpace(servicePort))
+ {
+ return null;
+ }
+
return $"https://{serviceHost}:{servicePort}";
}
}
From 60f21371bd83949268188037d6955dae4a6a56a8 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 25 May 2024 07:53:14 +0000
Subject: [PATCH 14/30] Update README
---
.../README.md | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index 9c516070fc..5d8ac0a367 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -36,14 +36,13 @@ your application is running:
## Kubernetes
-When running in a Kubernetes environment, the Container resource detector
-requires permissions to access pod information in order
-to extract the container ID.
-This is achieved using Kubernetes Role-Based Access Control (RBAC).
+To make container ID resolution work, container and pod name should be provided through KUBERNETES_CONTAINER_NAME and KUBERNETES_POD_NAME environment variable respectively and pod should have at least get permission to kubernetes resource pods.
+It can be done by utilizing YAML anchoring, downwards API and RBAC (Role-Based Access Control).
+
+If KUBERNETES_POD_NAME is not provided, detector will use HOSTNAME as a fallback, but it may not work in some environments or if hostname was overridden in pod spec.
+
+Below is an example of how to configure sample pod to make container ID resolution working:
-You will need to create a Role and RoleBinding to grant
-the necessary permissions to the service account used by your application.
-Below is an example of how to configure these RBAC resources:
```yaml
apiVersion: v1
@@ -92,6 +91,10 @@ spec:
env:
- name: KUBERNETES_CONTAINER_NAME
value: *container_name
+ - name: KUBERNETES_POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
```
## References
From e66dde2ae33cc5f3688af2da51513e10ff2d5313 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 25 May 2024 08:02:45 +0000
Subject: [PATCH 15/30] Fix README lint errors
---
.../README.md | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index 5d8ac0a367..772ebcb0a1 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -36,12 +36,19 @@ your application is running:
## Kubernetes
-To make container ID resolution work, container and pod name should be provided through KUBERNETES_CONTAINER_NAME and KUBERNETES_POD_NAME environment variable respectively and pod should have at least get permission to kubernetes resource pods.
-It can be done by utilizing YAML anchoring, downwards API and RBAC (Role-Based Access Control).
-
-If KUBERNETES_POD_NAME is not provided, detector will use HOSTNAME as a fallback, but it may not work in some environments or if hostname was overridden in pod spec.
-
-Below is an example of how to configure sample pod to make container ID resolution working:
+To make container ID resolution work, container and pod name should be provided
+through KUBERNETES_CONTAINER_NAME and KUBERNETES_POD_NAME environment variable
+respectively and pod should have at least
+get permission to kubernetes resource pods.
+It can be done by utilizing YAML anchoring, downwards API
+and RBAC (Role-Based Access Control).
+
+If KUBERNETES_POD_NAME is not provided, detector will use HOSTNAME
+as a fallback, but it may not work in some environments
+or if hostname was overridden in pod spec.
+
+Below is an example of how to configure sample pod
+to make container ID resolution working:
```yaml
From a96d991f6a7dfcf30a8be309c4d229d50ace0724 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 25 May 2024 08:02:58 +0000
Subject: [PATCH 16/30] Fast fail for non K8s environment
---
.../ContainerResourceDetector.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index e657f869e9..12853d28d0 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -70,7 +70,6 @@ public Resource Detect()
internal Resource BuildResource(string path, ParseMode parseMode)
{
var containerId = this.ExtractContainerId(path, parseMode);
-
if (string.IsNullOrEmpty(containerId))
{
return Resource.Empty;
@@ -134,7 +133,6 @@ internal Resource BuildResource(string path, ParseMode parseMode)
private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex, int endIndex)
{
startIndex = (startIndex == -1) ? 0 : startIndex + 1;
-
if (endIndex == -1)
{
endIndex = input.Length;
@@ -148,6 +146,11 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
try
{
var baseUrl = this.k8sMetadataFetcher.GetServiceBaseUrl();
+ if (string.IsNullOrWhiteSpace(baseUrl))
+ {
+ return null;
+ }
+
var @namespace = this.k8sMetadataFetcher.GetNamespace();
var hostname = this.k8sMetadataFetcher.GetPodName() ?? this.k8sMetadataFetcher.GetHostname();
var containerName = this.k8sMetadataFetcher.GetContainerName();
From 6f810af8fa056462b7d9a8bd8797cb4ec0e0af40 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 25 May 2024 08:07:02 +0000
Subject: [PATCH 17/30] Remove extra line
---
src/OpenTelemetry.ResourceDetectors.Container/README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/README.md b/src/OpenTelemetry.ResourceDetectors.Container/README.md
index 772ebcb0a1..824064d5e6 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/README.md
+++ b/src/OpenTelemetry.ResourceDetectors.Container/README.md
@@ -50,7 +50,6 @@ or if hostname was overridden in pod spec.
Below is an example of how to configure sample pod
to make container ID resolution working:
-
```yaml
apiVersion: v1
kind: ServiceAccount
From 2977bc7e66919e4c7652a2a60015f7338b5c3f9d Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 25 May 2024 19:57:52 +0000
Subject: [PATCH 18/30] Fast fail for non K8s env and container name not
provided
---
.../ContainerResourceDetector.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
index 57ff1a5c0a..6709db9c33 100644
--- a/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
+++ b/src/OpenTelemetry.ResourceDetectors.Container/ContainerResourceDetector.cs
@@ -146,14 +146,14 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
try
{
var baseUrl = this.k8sMetadataFetcher.GetServiceBaseUrl();
- if (string.IsNullOrEmpty(baseUrl))
+ var containerName = this.k8sMetadataFetcher.GetContainerName();
+ if (string.IsNullOrEmpty(baseUrl) && string.IsNullOrEmpty(containerName))
{
return null;
}
var @namespace = this.k8sMetadataFetcher.GetNamespace();
var hostname = this.k8sMetadataFetcher.GetPodName() ?? this.k8sMetadataFetcher.GetHostname();
- var containerName = this.k8sMetadataFetcher.GetContainerName();
var url = $"{baseUrl}/api/v1/namespaces/{@namespace}/pods/{hostname}";
var credentials = this.k8sMetadataFetcher.GetApiCredential();
if (string.IsNullOrEmpty(credentials))
From be15a322d8abec986ed8a45828f62446c6a7032b Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Tue, 4 Jun 2024 09:43:59 +0000
Subject: [PATCH 19/30] Fix EventSource name
---
.../ContainerResourceEventSource.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
index 333b206003..7982f80bca 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
@@ -7,7 +7,7 @@
namespace OpenTelemetry.Resources.Container;
-[EventSource(Name = "OpenTelemetry-ResourceDetectors-Container")]
+[EventSource(Name = "OpenTelemetry-Resources-Container")]
internal class ContainerResourceEventSource : EventSource, IServerCertificateValidationEventSource
{
public static ContainerResourceEventSource Log = new();
From f5c4122843c73d69c9f0e1fdc90c5644f293ad4d Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Tue, 4 Jun 2024 15:24:03 +0000
Subject: [PATCH 20/30] Fix CHANGELOG
---
src/OpenTelemetry.Resources.Container/CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/CHANGELOG.md b/src/OpenTelemetry.Resources.Container/CHANGELOG.md
index 140ebc63d0..2dd0092ea6 100644
--- a/src/OpenTelemetry.Resources.Container/CHANGELOG.md
+++ b/src/OpenTelemetry.Resources.Container/CHANGELOG.md
@@ -2,6 +2,9 @@
## Unreleased
+* Add Kubernetes support.
+ ([#1699](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1699))
+
## 1.0.0-beta.8
Released 2024-Jun-04
@@ -20,9 +23,6 @@ from `OpenTelemetry-ResourceDetectors-Container`
to `OpenTelemetry-Resources-Container`.
([#1849](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1849))
-* Add Kubernetes support.
- ([#1699](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1699))
-
## 1.0.0-beta.7
Released 2024-Apr-05
From 811ba2b8b61db47b3a6c2cc415722832fe3ff877 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Tue, 4 Jun 2024 15:48:11 +0000
Subject: [PATCH 21/30] Apply suggestions from review
---
src/OpenTelemetry.Resources.Container/ContainerDetector.cs | 2 +-
.../ContainerResourceEventSource.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index 7c63f069bc..73d13957f9 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -146,7 +146,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
{
var baseUrl = this.k8sMetadataFetcher.GetServiceBaseUrl();
var containerName = this.k8sMetadataFetcher.GetContainerName();
- if (string.IsNullOrEmpty(baseUrl) && string.IsNullOrEmpty(containerName))
+ if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(containerName))
{
return null;
}
diff --git a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
index 7982f80bca..446919dfcf 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
@@ -30,7 +30,7 @@ public void ExtractResourceAttributesException(string format, Exception ex)
[Event(EventIdFailedToExtractResourceAttributes, Message = "Failed to extract resource attributes in '{0}'.", Level = EventLevel.Error)]
public void FailedToExtractResourceAttributes(string format, string exception)
{
- this.WriteEvent(1, format, exception);
+ this.WriteEvent(EventIdFailedToExtractResourceAttributes, format, exception);
}
[Event(EventIdFailedToValidateCertificate, Message = "Failed to validate certificate. Details: '{0}'", Level = EventLevel.Warning)]
From 09d178589529d6e7e0f22fe9b3b3987d43e7a7e5 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 6 Jun 2024 06:36:58 +0000
Subject: [PATCH 22/30] Apply suggestions from review
---
.../ContainerDetector.cs | 126 ++++++++----------
.../K8sMetadataFetcher.cs | 20 +--
.../ParseMode.cs | 5 -
.../README.md | 4 +-
.../SourceGenerationContext.cs | 2 -
.../ContainerDetectorTests.cs | 24 ++--
...Telemetry.Resources.Container.Tests.csproj | 2 +-
7 files changed, 77 insertions(+), 106 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index 73d13957f9..8955e85c4a 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -46,37 +46,74 @@ internal ContainerDetector(IK8sMetadataFetcher k8sMetadataFetcher)
/// Resource with key-value pairs of resource attributes.
public Resource Detect()
{
- var resource = this.BuildResource(Filepath, ParseMode.K8s);
- if (resource == Resource.Empty)
+ var containerId = this.ExtractK8sContainerId();
+ if (!string.IsNullOrEmpty(containerId))
{
- resource = this.BuildResource(Filepath, ParseMode.V1);
+ return BuildResource(containerId);
}
- if (resource == Resource.Empty)
+ containerId = this.ExtractContainerId(Filepath, ParseMode.V1);
+ if (!string.IsNullOrEmpty(containerId))
{
- resource = this.BuildResource(FilepathV2, ParseMode.V2);
+ return BuildResource(containerId);
}
- return resource;
+ containerId = this.ExtractContainerId(FilepathV2, ParseMode.V2);
+ if (!string.IsNullOrEmpty(containerId))
+ {
+ return BuildResource(containerId);
+ }
+
+ return Resource.Empty;
+
+ static Resource BuildResource(string containerId)
+ {
+ return new Resource(new List>(1) { new(ContainerSemanticConventions.AttributeContainerId, containerId!) });
+ }
}
///
- /// Builds the resource attributes from Container Id in file path.
+ /// Extracts Container Id from path using the cgroupv1 format.
///
- /// File path where container id exists.
+ /// cgroup path.
/// CGroup Version of file to parse from.
- /// Returns Resource with list of key-value pairs of container resource attributes if container id exists else empty resource.
- internal Resource BuildResource(string path, ParseMode parseMode)
+ /// Container Id, if not found or exception being thrown.
+ internal string? ExtractContainerId(string path, ParseMode parseMode)
{
- var containerId = this.ExtractContainerId(path, parseMode);
- if (string.IsNullOrEmpty(containerId))
+ try
{
- return Resource.Empty;
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+
+ foreach (string line in File.ReadLines(path))
+ {
+ string? containerId = null;
+ if (!string.IsNullOrEmpty(line))
+ {
+ if (parseMode == ParseMode.V1)
+ {
+ containerId = GetIdFromLineV1(line);
+ }
+ else if (parseMode == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
+ {
+ containerId = GetIdFromLineV2(line);
+ }
+ }
+
+ if (!string.IsNullOrEmpty(containerId))
+ {
+ return containerId;
+ }
+ }
}
- else
+ catch (Exception ex)
{
- return new Resource(new List>(1) { new(ContainerSemanticConventions.AttributeContainerId, containerId!) });
+ ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerDetector)} : Failed to extract Container id from path", ex);
}
+
+ return null;
}
///
@@ -157,7 +194,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
var credentials = this.k8sMetadataFetcher.GetApiCredential();
if (string.IsNullOrEmpty(credentials))
{
- return string.Empty;
+ return null;
}
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
@@ -165,13 +202,13 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
var pod = DeserializeK8sResponse(response);
if (pod?.Status?.ContainerStatuses == null)
{
- return string.Empty;
+ return null;
}
var container = pod.Status.ContainerStatuses.SingleOrDefault(p => p.Name == containerName);
- if (container is null || string.IsNullOrEmpty(container.Id))
+ if (string.IsNullOrEmpty(container?.Id))
{
- return string.Empty;
+ return null;
}
// Container's ID is in :// format.
@@ -194,55 +231,4 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
#endif
}
}
-
- ///
- /// Extracts Container Id from path using the cgroupv1 format.
- ///
- /// cgroup path.
- /// CGroup Version of file to parse from.
- /// Container Id, if not found or exception being thrown.
- private string? ExtractContainerId(string path, ParseMode parseMode)
- {
- try
- {
- if (parseMode == ParseMode.K8s)
- {
- return this.ExtractK8sContainerId();
- }
- else
- {
- if (!File.Exists(path))
- {
- return null;
- }
-
- foreach (string line in File.ReadLines(path))
- {
- string? containerId = null;
- if (!string.IsNullOrEmpty(line))
- {
- if (parseMode == ParseMode.V1)
- {
- containerId = GetIdFromLineV1(line);
- }
- else if (parseMode == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
- {
- containerId = GetIdFromLineV2(line);
- }
- }
-
- if (!string.IsNullOrEmpty(containerId))
- {
- return containerId;
- }
- }
- }
- }
- catch (Exception ex)
- {
- ContainerResourceEventSource.Log.ExtractResourceAttributesException($"{nameof(ContainerDetector)} : Failed to extract Container id from path", ex);
- }
-
- return null;
- }
}
diff --git a/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs b/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
index 8a77db3e43..3007c112e8 100644
--- a/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
+++ b/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
@@ -9,11 +9,11 @@ namespace OpenTelemetry.Resources.Container;
internal sealed class K8sMetadataFetcher : IK8sMetadataFetcher
{
- private const string KubernetesServiceHostKey = "KUBERNETES_SERVICE_HOST";
- private const string KubernetesServicePortKey = "KUBERNETES_SERVICE_PORT_HTTPS";
- private const string KubernetesHostnameKey = "HOSTNAME";
- private const string KubernetesPodNameKey = "KUBERNETES_POD_NAME";
- private const string KubernetesContainerNameKey = "KUBERNETES_CONTAINER_NAME";
+ private const string KubernetesServiceHostEnvVar = "KUBERNETES_SERVICE_HOST";
+ private const string KubernetesServicePortEnvVar = "KUBERNETES_SERVICE_PORT_HTTPS";
+ private const string KubernetesHostnameEnvVar = "HOSTNAME";
+ private const string KubernetesPodNameEnvVar = "KUBERNETES_POD_NAME";
+ private const string KubernetesContainerNameEnvVar = "KUBERNETES_CONTAINER_NAME";
private const string KubernetesNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
private const string KubernetesCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
@@ -43,17 +43,17 @@ internal sealed class K8sMetadataFetcher : IK8sMetadataFetcher
public string? GetContainerName()
{
- return Environment.GetEnvironmentVariable(KubernetesContainerNameKey);
+ return Environment.GetEnvironmentVariable(KubernetesContainerNameEnvVar);
}
public string? GetHostname()
{
- return Environment.GetEnvironmentVariable(KubernetesHostnameKey);
+ return Environment.GetEnvironmentVariable(KubernetesHostnameEnvVar);
}
public string? GetPodName()
{
- return Environment.GetEnvironmentVariable(KubernetesPodNameKey);
+ return Environment.GetEnvironmentVariable(KubernetesPodNameEnvVar);
}
public string? GetNamespace()
@@ -63,8 +63,8 @@ internal sealed class K8sMetadataFetcher : IK8sMetadataFetcher
public string? GetServiceBaseUrl()
{
- var serviceHost = Environment.GetEnvironmentVariable(KubernetesServiceHostKey);
- var servicePort = Environment.GetEnvironmentVariable(KubernetesServicePortKey);
+ var serviceHost = Environment.GetEnvironmentVariable(KubernetesServiceHostEnvVar);
+ var servicePort = Environment.GetEnvironmentVariable(KubernetesServicePortEnvVar);
if (string.IsNullOrWhiteSpace(serviceHost) || string.IsNullOrWhiteSpace(servicePort))
{
diff --git a/src/OpenTelemetry.Resources.Container/ParseMode.cs b/src/OpenTelemetry.Resources.Container/ParseMode.cs
index 7f712ec9a0..5a3ce66bf7 100644
--- a/src/OpenTelemetry.Resources.Container/ParseMode.cs
+++ b/src/OpenTelemetry.Resources.Container/ParseMode.cs
@@ -17,9 +17,4 @@ internal enum ParseMode
/// Represents CGroupV2.
///
V2,
-
- ///
- /// Represents Kubernetes.
- ///
- K8s,
}
diff --git a/src/OpenTelemetry.Resources.Container/README.md b/src/OpenTelemetry.Resources.Container/README.md
index 8b5a2760d9..f9b128870d 100644
--- a/src/OpenTelemetry.Resources.Container/README.md
+++ b/src/OpenTelemetry.Resources.Container/README.md
@@ -38,13 +38,13 @@ your application is running:
## Kubernetes
To make container ID resolution work, container and pod name should be provided
-through KUBERNETES_CONTAINER_NAME and KUBERNETES_POD_NAME environment variable
+through `KUBERNETES_CONTAINER_NAME` and `KUBERNETES_POD_NAME` environment variable
respectively and pod should have at least
get permission to kubernetes resource pods.
It can be done by utilizing YAML anchoring, downwards API
and RBAC (Role-Based Access Control).
-If KUBERNETES_POD_NAME is not provided, detector will use HOSTNAME
+If `KUBERNETES_POD_NAME` is not provided, detector will use `HOSTNAME`
as a fallback, but it may not work in some environments
or if hostname was overridden in pod spec.
diff --git a/src/OpenTelemetry.Resources.Container/SourceGenerationContext.cs b/src/OpenTelemetry.Resources.Container/SourceGenerationContext.cs
index 9653854db8..22cb03deb1 100644
--- a/src/OpenTelemetry.Resources.Container/SourceGenerationContext.cs
+++ b/src/OpenTelemetry.Resources.Container/SourceGenerationContext.cs
@@ -1,7 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-#if NET6_0_OR_GREATER
using System.Text.Json.Serialization;
using OpenTelemetry.Resources.Container.Models;
@@ -21,4 +20,3 @@ namespace OpenTelemetry.Resources.Container;
internal sealed partial class SourceGenerationContext : JsonSerializerContext
{
}
-#endif
diff --git a/test/OpenTelemetry.Resources.Container.Tests/ContainerDetectorTests.cs b/test/OpenTelemetry.Resources.Container.Tests/ContainerDetectorTests.cs
index 6fefd6e2ff..d865a8b7ef 100644
--- a/test/OpenTelemetry.Resources.Container.Tests/ContainerDetectorTests.cs
+++ b/test/OpenTelemetry.Resources.Container.Tests/ContainerDetectorTests.cs
@@ -99,7 +99,7 @@ public void TestValidContainer()
tempFile.Write(testCase.Line);
Assert.Equal(
testCase.ExpectedContainerId,
- GetContainerId(containerDetector.BuildResource(tempFile.FilePath, testCase.CgroupVersion)));
+ containerDetector.ExtractContainerId(tempFile.FilePath, testCase.CgroupVersion));
}
}
@@ -113,9 +113,8 @@ public void TestInvalidContainer()
{
using var tempFile = new TempFile();
tempFile.Write(testCase.Line);
- Assert.Equal(
- containerDetector.BuildResource(tempFile.FilePath, ParseMode.V2),
- Resource.Empty);
+ Assert.Null(
+ containerDetector.ExtractContainerId(tempFile.FilePath, ParseMode.V2));
}
// Valid in cgroupv1 is not valid in cgroupv1
@@ -123,9 +122,8 @@ public void TestInvalidContainer()
{
using var tempFile = new TempFile();
tempFile.Write(testCase.Line);
- Assert.Equal(
- containerDetector.BuildResource(tempFile.FilePath, ParseMode.V1),
- Resource.Empty);
+ Assert.Null(
+ containerDetector.ExtractContainerId(tempFile.FilePath, ParseMode.V1));
}
// test invalid cases
@@ -133,12 +131,12 @@ public void TestInvalidContainer()
{
using var tempFile = new TempFile();
tempFile.Write(testCase.Line);
- Assert.Equal(containerDetector.BuildResource(tempFile.FilePath, testCase.CgroupVersion), Resource.Empty);
+ Assert.Null(containerDetector.ExtractContainerId(tempFile.FilePath, testCase.CgroupVersion));
}
// test invalid file
- Assert.Equal(containerDetector.BuildResource(Path.GetTempPath(), ParseMode.V1), Resource.Empty);
- Assert.Equal(containerDetector.BuildResource(Path.GetTempPath(), ParseMode.V2), Resource.Empty);
+ Assert.Null(containerDetector.ExtractContainerId(Path.GetTempPath(), ParseMode.V1));
+ Assert.Null(containerDetector.ExtractContainerId(Path.GetTempPath(), ParseMode.V2));
}
[Fact]
@@ -152,12 +150,6 @@ public async Task TestK8sContainerId()
}
}
- private static string GetContainerId(Resource resource)
- {
- var resourceAttributes = resource.Attributes.ToDictionary(x => x.Key, x => x.Value);
- return resourceAttributes[ContainerSemanticConventions.AttributeContainerId].ToString()!;
- }
-
private sealed class TestCase
{
public TestCase(string name, string line, ParseMode cgroupVersion, string? expectedContainerId = null)
diff --git a/test/OpenTelemetry.Resources.Container.Tests/OpenTelemetry.Resources.Container.Tests.csproj b/test/OpenTelemetry.Resources.Container.Tests/OpenTelemetry.Resources.Container.Tests.csproj
index 0c9ba70cf8..3ff5eb23e3 100644
--- a/test/OpenTelemetry.Resources.Container.Tests/OpenTelemetry.Resources.Container.Tests.csproj
+++ b/test/OpenTelemetry.Resources.Container.Tests/OpenTelemetry.Resources.Container.Tests.csproj
@@ -7,7 +7,7 @@
enable
-
+
PreserveNewest
From 5442c3ff74576dc61e313178ffcd452726631122 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 6 Jun 2024 06:56:51 +0000
Subject: [PATCH 23/30] Simplify deserialization
---
.../ContainerDetector.cs | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index 8955e85c4a..d7174e1b43 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -199,7 +199,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
- var pod = DeserializeK8sResponse(response);
+ var pod = ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.K8sPod);
if (pod?.Status?.ContainerStatuses == null)
{
return null;
@@ -221,14 +221,5 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
}
return null;
-
- static K8sPod? DeserializeK8sResponse(string response)
- {
-#if NET6_0_OR_GREATER
- return ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.K8sPod);
-#else
- return ResourceDetectorUtils.DeserializeFromString(response);
-#endif
- }
}
}
From 4dd428dce9d19e319dfad958364aaa9ef7cf33d0 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 6 Jun 2024 07:01:49 +0000
Subject: [PATCH 24/30] Remove unnecessary using
---
src/OpenTelemetry.Resources.Container/ContainerDetector.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index d7174e1b43..14c6538b03 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -6,7 +6,6 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
-using OpenTelemetry.Resources.Container.Models;
using OpenTelemetry.Resources.Container.Utils;
namespace OpenTelemetry.Resources.Container;
From 72463c5a2f56548239f6d01247c88612387ccbee Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Fri, 28 Jun 2024 06:31:26 +0000
Subject: [PATCH 25/30] Remove unnecessary usings
---
.../ContainerResourceEventSource.cs | 1 -
src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs | 2 --
src/OpenTelemetry.Resources.Container/Models/K8sPodStatus.cs | 1 -
3 files changed, 4 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
index 446919dfcf..db553254cc 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
@@ -1,7 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-using System;
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;
diff --git a/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs b/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
index 3007c112e8..2ff6c58d05 100644
--- a/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
+++ b/src/OpenTelemetry.Resources.Container/K8sMetadataFetcher.cs
@@ -1,8 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-using System;
-using System.IO;
using System.Text;
namespace OpenTelemetry.Resources.Container;
diff --git a/src/OpenTelemetry.Resources.Container/Models/K8sPodStatus.cs b/src/OpenTelemetry.Resources.Container/Models/K8sPodStatus.cs
index 66dc04fa29..7451635af3 100644
--- a/src/OpenTelemetry.Resources.Container/Models/K8sPodStatus.cs
+++ b/src/OpenTelemetry.Resources.Container/Models/K8sPodStatus.cs
@@ -1,7 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace OpenTelemetry.Resources.Container.Models;
From 0c3bad3363020fadcea982512d7cb3e59f64bb4a Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Fri, 28 Jun 2024 06:31:54 +0000
Subject: [PATCH 26/30] Move and use AsyncHelper
---
opentelemetry-dotnet-contrib.sln | 1 +
.../OpenTelemetry.Resources.AWS.csproj | 1 +
src/OpenTelemetry.Resources.Container/ContainerDetector.cs | 2 +-
.../OpenTelemetry.Resources.Container.csproj | 1 +
src/{OpenTelemetry.Resources.AWS => Shared}/AsyncHelper.cs | 2 +-
5 files changed, 5 insertions(+), 2 deletions(-)
rename src/{OpenTelemetry.Resources.AWS => Shared}/AsyncHelper.cs (97%)
diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln
index dc47d5de4d..0a13cfdd87 100644
--- a/opentelemetry-dotnet-contrib.sln
+++ b/opentelemetry-dotnet-contrib.sln
@@ -238,6 +238,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{1FCC8E
ProjectSection(SolutionItems) = preProject
src\Shared\ActivityInstrumentationHelper.cs = src\Shared\ActivityInstrumentationHelper.cs
src\Shared\AssemblyVersionExtensions.cs = src\Shared\AssemblyVersionExtensions.cs
+ src\Shared\AsyncHelper.cs = src\Shared\AsyncHelper.cs
src\Shared\DiagnosticSourceListener.cs = src\Shared\DiagnosticSourceListener.cs
src\Shared\DiagnosticSourceSubscriber.cs = src\Shared\DiagnosticSourceSubscriber.cs
src\Shared\ExceptionExtensions.cs = src\Shared\ExceptionExtensions.cs
diff --git a/src/OpenTelemetry.Resources.AWS/OpenTelemetry.Resources.AWS.csproj b/src/OpenTelemetry.Resources.AWS/OpenTelemetry.Resources.AWS.csproj
index 010589d8d7..0c889615ed 100644
--- a/src/OpenTelemetry.Resources.AWS/OpenTelemetry.Resources.AWS.csproj
+++ b/src/OpenTelemetry.Resources.AWS/OpenTelemetry.Resources.AWS.csproj
@@ -27,6 +27,7 @@
+
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index d94e1ba1bf..4b44bef915 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -193,7 +193,7 @@ private static string RemovePrefixAndSuffixIfNeeded(string input, int startIndex
}
using var httpClientHandler = ServerCertificateValidationHandler.Create(K8sCertificatePath, ContainerResourceEventSource.Log);
- var response = ResourceDetectorUtils.SendOutRequest(url, "GET", new KeyValuePair("Authorization", credentials), httpClientHandler).GetAwaiter().GetResult();
+ var response = AsyncHelper.RunSync(() => ResourceDetectorUtils.SendOutRequestAsync(url, HttpMethod.Get, new KeyValuePair("Authorization", credentials), httpClientHandler));
var pod = ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.K8sPod);
if (pod?.Status?.ContainerStatuses == null)
{
diff --git a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
index 588093080b..5af14b167e 100644
--- a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
+++ b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
@@ -17,6 +17,7 @@
+
diff --git a/src/OpenTelemetry.Resources.AWS/AsyncHelper.cs b/src/Shared/AsyncHelper.cs
similarity index 97%
rename from src/OpenTelemetry.Resources.AWS/AsyncHelper.cs
rename to src/Shared/AsyncHelper.cs
index f999beff0e..3a34165ff5 100644
--- a/src/OpenTelemetry.Resources.AWS/AsyncHelper.cs
+++ b/src/Shared/AsyncHelper.cs
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-namespace OpenTelemetry.Resources.AWS;
+namespace OpenTelemetry.Resources;
///
/// A helper class for running asynchronous methods synchronously.
From fed42832f28d514a97524ad652a433f0635996f4 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman2@users.noreply.github.com>
Date: Tue, 6 Aug 2024 11:27:33 +0000
Subject: [PATCH 27/30] Update
src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
Co-authored-by: Weihan Li
---
.../ContainerResourceEventSource.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
index db553254cc..5283956384 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerResourceEventSource.cs
@@ -7,7 +7,7 @@
namespace OpenTelemetry.Resources.Container;
[EventSource(Name = "OpenTelemetry-Resources-Container")]
-internal class ContainerResourceEventSource : EventSource, IServerCertificateValidationEventSource
+internal sealed class ContainerResourceEventSource : EventSource, IServerCertificateValidationEventSource
{
public static ContainerResourceEventSource Log = new();
From 8f72ae3bdc89e9f79305feb3fe05c61f6cf410b2 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 5 Oct 2024 09:03:56 +0000
Subject: [PATCH 28/30] Few fixes following .NET Standard 2.0
---
.../ContainerDetector.cs | 4 ++--
.../OpenTelemetry.Resources.Container.csproj | 6 +++++
src/Shared/ResourceDetectorUtils.cs | 22 +++++++++----------
3 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
index 2eaf3b584b..d86d621b6b 100644
--- a/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
+++ b/src/OpenTelemetry.Resources.Container/ContainerDetector.cs
@@ -92,9 +92,9 @@ static Resource BuildResource(string containerId)
containerId = GetIdFromLineV1(line);
}
#if NET
- else if (cgroupVersion == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
+ else if (parseMode == ParseMode.V2 && line.Contains(Hostname, StringComparison.Ordinal))
#else
- else if (cgroupVersion == ParseMode.V2 && line.Contains(Hostname))
+ else if (parseMode == ParseMode.V2 && line.Contains(Hostname))
#endif
{
containerId = GetIdFromLineV2(line);
diff --git a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
index d1c096ebf5..d687f8b762 100644
--- a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
+++ b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
@@ -7,6 +7,11 @@
Resources.Container-
+
+
+ $(NoWarn);nullable
+
+
@@ -15,6 +20,7 @@
+
diff --git a/src/Shared/ResourceDetectorUtils.cs b/src/Shared/ResourceDetectorUtils.cs
index 422f71f55a..5b89c51cfb 100644
--- a/src/Shared/ResourceDetectorUtils.cs
+++ b/src/Shared/ResourceDetectorUtils.cs
@@ -6,7 +6,7 @@
#endif
using System.Text;
using System.Text.Json;
-#if NET
+#if !NETFRAMEWORK
using System.Text.Json.Serialization.Metadata;
#endif
@@ -17,7 +17,7 @@ namespace OpenTelemetry.Resources;
///
internal static class ResourceDetectorUtils
{
-#if !NET
+#if NETFRAMEWORK
private static readonly JsonSerializerOptions JsonSerializerOptions = new(JsonSerializerDefaults.Web);
#endif
@@ -47,31 +47,31 @@ internal static async Task SendOutRequestAsync(
}
}
-#if NET
- internal static T? DeserializeFromFile(string filePath, JsonTypeInfo jsonTypeInfo)
+#if NETFRAMEWORK
+ internal static T? DeserializeFromFile(string filePath)
{
using (var stream = GetStream(filePath))
{
- return JsonSerializer.Deserialize(stream, jsonTypeInfo);
+ return JsonSerializer.Deserialize(stream, JsonSerializerOptions);
}
}
- internal static T? DeserializeFromString(string json, JsonTypeInfo jsonTypeInfo)
+ internal static T? DeserializeFromString(string json)
{
- return JsonSerializer.Deserialize(json, jsonTypeInfo);
+ return JsonSerializer.Deserialize(json, JsonSerializerOptions);
}
#else
- internal static T? DeserializeFromFile(string filePath)
+ internal static T? DeserializeFromFile(string filePath, JsonTypeInfo jsonTypeInfo)
{
using (var stream = GetStream(filePath))
{
- return JsonSerializer.Deserialize(stream, JsonSerializerOptions);
+ return JsonSerializer.Deserialize(stream, jsonTypeInfo);
}
}
- internal static T? DeserializeFromString(string json)
+ internal static T? DeserializeFromString(string json, JsonTypeInfo jsonTypeInfo)
{
- return JsonSerializer.Deserialize(json, JsonSerializerOptions);
+ return JsonSerializer.Deserialize(json, jsonTypeInfo);
}
#endif
From c81dd58df66e9322103f8bed5940450f24c34211 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Sat, 5 Oct 2024 09:19:49 +0000
Subject: [PATCH 29/30] Additional fixes
---
src/OpenTelemetry.Resources.AWS/AWSEBSDetector.cs | 6 +++---
src/OpenTelemetry.Resources.AWS/AWSEC2Detector.cs | 6 +++---
src/OpenTelemetry.Resources.AWS/SourceGenerationContext.cs | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/OpenTelemetry.Resources.AWS/AWSEBSDetector.cs b/src/OpenTelemetry.Resources.AWS/AWSEBSDetector.cs
index c17c291add..80acea1404 100644
--- a/src/OpenTelemetry.Resources.AWS/AWSEBSDetector.cs
+++ b/src/OpenTelemetry.Resources.AWS/AWSEBSDetector.cs
@@ -84,10 +84,10 @@ internal static List> ExtractResourceAttributes(AWS
internal static AWSEBSMetadataModel? GetEBSMetadata(string filePath)
{
-#if NET
- return ResourceDetectorUtils.DeserializeFromFile(filePath, SourceGenerationContext.Default.AWSEBSMetadataModel);
-#else
+#if NETFRAMEWORK
return ResourceDetectorUtils.DeserializeFromFile(filePath);
+#else
+ return ResourceDetectorUtils.DeserializeFromFile(filePath, SourceGenerationContext.Default.AWSEBSMetadataModel);
#endif
}
}
diff --git a/src/OpenTelemetry.Resources.AWS/AWSEC2Detector.cs b/src/OpenTelemetry.Resources.AWS/AWSEC2Detector.cs
index 4d41c33338..076adbf48d 100644
--- a/src/OpenTelemetry.Resources.AWS/AWSEC2Detector.cs
+++ b/src/OpenTelemetry.Resources.AWS/AWSEC2Detector.cs
@@ -83,10 +83,10 @@ internal static List> ExtractResourceAttributes(AWS
internal static AWSEC2IdentityDocumentModel? DeserializeResponse(string response)
{
-#if NET
- return ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.AWSEC2IdentityDocumentModel);
-#else
+#if NETFRAMEWORK
return ResourceDetectorUtils.DeserializeFromString(response);
+#else
+ return ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.AWSEC2IdentityDocumentModel);
#endif
}
diff --git a/src/OpenTelemetry.Resources.AWS/SourceGenerationContext.cs b/src/OpenTelemetry.Resources.AWS/SourceGenerationContext.cs
index 51f0b9a4d5..8857095023 100644
--- a/src/OpenTelemetry.Resources.AWS/SourceGenerationContext.cs
+++ b/src/OpenTelemetry.Resources.AWS/SourceGenerationContext.cs
@@ -1,7 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
-#if NET
+#if !NETFRAMEWORK
using System.Text.Json.Serialization;
using OpenTelemetry.Resources.AWS.Models;
From 1f7d8f5808bd007bf7e664ebb2a840fffb26ef20 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 17 Oct 2024 06:04:07 +0000
Subject: [PATCH 30/30] Fix STJ version
---
.../OpenTelemetry.Resources.Container.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
index d687f8b762..1093920143 100644
--- a/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
+++ b/src/OpenTelemetry.Resources.Container/OpenTelemetry.Resources.Container.csproj
@@ -20,7 +20,7 @@
-
+