-
Notifications
You must be signed in to change notification settings - Fork 381
Expand file tree
/
Copy pathAWSEKSDetector.cs
More file actions
151 lines (130 loc) · 5.58 KB
/
AWSEKSDetector.cs
File metadata and controls
151 lines (130 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#if NET
using System.Text;
using OpenTelemetry.AWS;
using OpenTelemetry.Resources.AWS.Models;
namespace OpenTelemetry.Resources.AWS;
/// <summary>
/// Resource detector for application running in AWS EKS.
/// </summary>
internal sealed class AWSEKSDetector : IResourceDetector
{
private const string AWSEKSCertificatePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
private const string AWSEKSCredentialPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
private const string AWSEKSMetadataFilePath = "/proc/self/cgroup";
private const string AWSClusterInfoUrl = "https://kubernetes.default.svc/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info";
private const string AWSAuthUrl = "https://kubernetes.default.svc/api/v1/namespaces/kube-system/configmaps/aws-auth";
private readonly AWSSemanticConventions semanticConventionBuilder;
public AWSEKSDetector(AWSSemanticConventions semanticConventionBuilder)
{
this.semanticConventionBuilder = semanticConventionBuilder;
}
/// <summary>
/// Detector the required and optional resource attributes from AWS EKS.
/// </summary>
/// <returns>Resource with key-value pairs of resource attributes.</returns>
public Resource Detect()
{
var credentials = GetEKSCredentials(AWSEKSCredentialPath);
using var httpClientHandler = ServerCertificateValidationHandler.Create(AWSEKSCertificatePath, AWSResourcesEventSource.Log);
return credentials == null || !IsEKSProcess(credentials, httpClientHandler)
? Resource.Empty
: new Resource(this.ExtractResourceAttributes(
GetEKSClusterName(credentials, httpClientHandler),
GetEKSContainerId(AWSEKSMetadataFilePath)));
}
internal static string? GetEKSCredentials(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)
{
AWSResourcesEventSource.Log.ResourceAttributesExtractException($"{nameof(AWSEKSDetector)} : Failed to load client token", ex);
}
return null;
}
internal static string? GetEKSContainerId(string path)
{
try
{
using var streamReader = ResourceDetectorUtils.GetStreamReader(path);
while (!streamReader.EndOfStream)
{
var trimmedLine = streamReader.ReadLine()?.Trim();
if (trimmedLine?.Length > 64)
{
return trimmedLine.Substring(trimmedLine.Length - 64);
}
}
}
catch (Exception ex)
{
AWSResourcesEventSource.Log.ResourceAttributesExtractException($"{nameof(AWSEKSDetector)} : Failed to get Container Id", ex);
}
return null;
}
internal static AWSEKSClusterInformationModel? DeserializeResponse(string response)
{
#if NET
return ResourceDetectorUtils.DeserializeFromString(response, SourceGenerationContext.Default.AWSEKSClusterInformationModel);
#else
return ResourceDetectorUtils.DeserializeFromString<AWSEKSClusterInformationModel>(response);
#endif
}
internal List<KeyValuePair<string, object>> ExtractResourceAttributes(string? clusterName, string? containerId)
{
var resourceAttributes =
this.semanticConventionBuilder
.AttributeBuilder
.AddAttributeCloudProviderIsAWS()
.AddAttributeCloudPlatformIsAwsEks()
.AddAttributeK8SClusterName(clusterName)
.AddAttributeContainerId(containerId)
.Build();
return resourceAttributes;
}
private static string? GetEKSClusterName(string credentials, HttpClientHandler? httpClientHandler)
{
try
{
var clusterInfo = GetEKSClusterInfo(credentials, httpClientHandler);
return DeserializeResponse(clusterInfo)?.Data?.ClusterName;
}
catch (Exception ex)
{
AWSResourcesEventSource.Log.ResourceAttributesExtractException($"{nameof(AWSEKSDetector)} : Failed to get cluster information", ex);
}
return null;
}
private static bool IsEKSProcess(string credentials, HttpClientHandler? httpClientHandler)
{
string? awsAuth = null;
try
{
using var scope = SuppressInstrumentationScope.Begin();
awsAuth = AsyncHelper.RunSync(() => ResourceDetectorUtils.SendOutRequestAsync(AWSAuthUrl, HttpMethod.Get, new KeyValuePair<string, string>("Authorization", credentials), httpClientHandler));
}
catch (Exception ex)
{
AWSResourcesEventSource.Log.ResourceAttributesExtractException($"{nameof(AWSEKSDetector)} : Failed to get EKS information", ex);
}
return !string.IsNullOrEmpty(awsAuth);
}
private static string GetEKSClusterInfo(string credentials, HttpClientHandler? httpClientHandler)
{
using var scope = SuppressInstrumentationScope.Begin();
return AsyncHelper.RunSync(() => ResourceDetectorUtils.SendOutRequestAsync(AWSClusterInfoUrl, HttpMethod.Get, new KeyValuePair<string, string>("Authorization", credentials), httpClientHandler));
}
}
#endif