@@ -20,12 +20,15 @@ package add_cloud_metadata
20
20
import (
21
21
"context"
22
22
"fmt"
23
+ "io"
23
24
"net/http"
25
+ "strings"
24
26
25
27
"github.com/elastic/elastic-agent-libs/logp"
26
28
27
29
awssdk "github.com/aws/aws-sdk-go-v2/aws"
28
30
awscfg "github.com/aws/aws-sdk-go-v2/config"
31
+ "github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds"
29
32
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
30
33
"github.com/aws/aws-sdk-go-v2/service/ec2"
31
34
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
@@ -35,7 +38,14 @@ import (
35
38
conf "github.com/elastic/elastic-agent-libs/config"
36
39
)
37
40
41
+ const (
42
+ eksClusterNameTagKey = "eks:cluster-name"
43
+ tagsCategory = "tags/instance"
44
+ tagPrefix = "aws.tags"
45
+ )
46
+
38
47
type IMDSClient interface {
48
+ ec2rolecreds.GetMetadataAPIClient
39
49
GetInstanceIdentityDocument (ctx context.Context , params * imds.GetInstanceIdentityDocumentInput , optFns ... func (* imds.Options )) (* imds.GetInstanceIdentityDocumentOutput , error )
40
50
}
41
51
@@ -90,30 +100,17 @@ func fetchRawProviderMetadata(
90
100
result .err = fmt .Errorf ("failed loading AWS default configuration: %w" , err )
91
101
return
92
102
}
93
- awsClient := NewIMDSClient (awsConfig )
94
103
95
- instanceIdentity , err := awsClient .GetInstanceIdentityDocument (context .TODO (), & imds.GetInstanceIdentityDocumentInput {})
104
+ imdsClient := NewIMDSClient (awsConfig )
105
+ instanceIdentity , err := imdsClient .GetInstanceIdentityDocument (ctx , & imds.GetInstanceIdentityDocumentInput {})
96
106
if err != nil {
97
107
result .err = fmt .Errorf ("failed fetching EC2 Identity Document: %w" , err )
98
108
return
99
109
}
100
110
101
- // AWS Region must be set to be able to get EC2 Tags
102
111
awsRegion := instanceIdentity .InstanceIdentityDocument .Region
103
- awsConfig .Region = awsRegion
104
112
accountID := instanceIdentity .InstanceIdentityDocument .AccountID
105
-
106
- clusterName , err := fetchEC2ClusterNameTag (awsConfig , instanceIdentity .InstanceIdentityDocument .InstanceID )
107
- if err != nil {
108
- logger .Warnf ("error fetching cluster name metadata: %s." , err )
109
- } else if clusterName != "" {
110
- // for AWS cluster ID is used cluster ARN: arn:partition:service:region:account-id:resource-type/resource-id, example:
111
- // arn:aws:eks:us-east-2:627286350134:cluster/cluster-name
112
- clusterARN := fmt .Sprintf ("arn:aws:eks:%s:%s:cluster/%v" , awsRegion , accountID , clusterName )
113
-
114
- _ , _ = result .metadata .Put ("orchestrator.cluster.id" , clusterARN )
115
- _ , _ = result .metadata .Put ("orchestrator.cluster.name" , clusterName )
116
- }
113
+ instanceID := instanceIdentity .InstanceIdentityDocument .InstanceID
117
114
118
115
_ , _ = result .metadata .Put ("cloud.instance.id" , instanceIdentity .InstanceIdentityDocument .InstanceID )
119
116
_ , _ = result .metadata .Put ("cloud.machine.type" , instanceIdentity .InstanceIdentityDocument .InstanceType )
@@ -122,10 +119,106 @@ func fetchRawProviderMetadata(
122
119
_ , _ = result .metadata .Put ("cloud.account.id" , accountID )
123
120
_ , _ = result .metadata .Put ("cloud.image.id" , instanceIdentity .InstanceIdentityDocument .ImageID )
124
121
122
+ // AWS Region must be set to be able to get EC2 Tags
123
+ awsConfig .Region = awsRegion
124
+ tags := getTags (ctx , imdsClient , NewEC2Client (awsConfig ), instanceID , logger )
125
+
126
+ if tags [eksClusterNameTagKey ] != "" {
127
+ // for AWS cluster ID is used cluster ARN: arn:partition:service:region:account-id:resource-type/resource-id, example:
128
+ // arn:aws:eks:us-east-2:627286350134:cluster/cluster-name
129
+ clusterARN := fmt .Sprintf ("arn:aws:eks:%s:%s:cluster/%v" , awsRegion , accountID , tags [eksClusterNameTagKey ])
130
+
131
+ _ , _ = result .metadata .Put ("orchestrator.cluster.id" , clusterARN )
132
+ _ , _ = result .metadata .Put ("orchestrator.cluster.name" , tags [eksClusterNameTagKey ])
133
+ }
134
+
135
+ if len (tags ) == 0 {
136
+ return
137
+ }
138
+
139
+ logger .Infof ("Adding retrieved tags with key: %s" , tagPrefix )
140
+ for k , v := range tags {
141
+ _ , _ = result .metadata .Put (fmt .Sprintf ("%s.%s" , tagPrefix , k ), v )
142
+ }
143
+ }
144
+
145
+ // getTags is a helper to extract EC2 tags. Internally it utilize multiple extraction methods.
146
+ func getTags (ctx context.Context , imdsClient IMDSClient , ec2Client EC2Client , instanceId string , logger * logp.Logger ) map [string ]string {
147
+ logger .Info ("Extracting EC2 tags from IMDS endpoint" )
148
+ tags , ok := getTagsFromIMDS (ctx , imdsClient , logger )
149
+ if ok {
150
+ return tags
151
+ }
152
+
153
+ logger .Info ("Tag extraction from IMDS failed, fallback to DescribeTags API to obtain EKS cluster name." )
154
+ clusterName , err := clusterNameFromDescribeTag (ctx , ec2Client , instanceId )
155
+ if err != nil {
156
+ logger .Warnf ("error obtaining cluster name: %v." , err )
157
+ return tags
158
+ }
159
+
160
+ if clusterName != "" {
161
+ tags [eksClusterNameTagKey ] = clusterName
162
+ }
163
+ return tags
164
+ }
165
+
166
+ // getTagsFromIMDS is a helper to extract EC2 tags using instance metadata service.
167
+ // Note that this call could get throttled and currently does not implement a retry mechanism.
168
+ // See - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#instancedata-throttling
169
+ func getTagsFromIMDS (ctx context.Context , client IMDSClient , logger * logp.Logger ) (tags map [string ]string , ok bool ) {
170
+ tags = make (map [string ]string )
171
+
172
+ b , err := getMetadataHelper (ctx , client , tagsCategory , logger )
173
+ if err != nil {
174
+ logger .Warnf ("error obtaining tags category: %v" , err )
175
+ return tags , false
176
+ }
177
+
178
+ for _ , tag := range strings .Split (string (b ), "\n " ) {
179
+ tagPath := fmt .Sprintf ("%s/%s" , tagsCategory , tag )
180
+ b , err := getMetadataHelper (ctx , client , tagPath , logger )
181
+ if err != nil {
182
+ logger .Warnf ("error extracting tag value of %s: %v" , tag , err )
183
+ return tags , false
184
+ }
185
+
186
+ tagValue := string (b )
187
+ if tagValue == "" {
188
+ logger .Infof ("Ignoring tag key %s as value is empty" , tag )
189
+ continue
190
+ }
191
+
192
+ tags [tag ] = tagValue
193
+ }
194
+
195
+ return tags , true
196
+ }
197
+
198
+ // getMetadataHelper performs the IMDS call for the given path and returns the response content after closing the underlying content reader.
199
+ func getMetadataHelper (ctx context.Context , client IMDSClient , path string , logger * logp.Logger ) (content []byte , err error ) {
200
+ metadata , err := client .GetMetadata (ctx , & imds.GetMetadataInput {Path : path })
201
+ if err != nil {
202
+ return nil , fmt .Errorf ("error from IMDS metadata request: %w" , err )
203
+ }
204
+
205
+ defer func (Content io.ReadCloser ) {
206
+ err := Content .Close ()
207
+ if err != nil {
208
+ logger .Warnf ("error closing IMDS metadata response body: %v" , err )
209
+ }
210
+ }(metadata .Content )
211
+
212
+ content , err = io .ReadAll (metadata .Content )
213
+ if err != nil {
214
+ return nil , fmt .Errorf ("error extracting metadata from the IMDS response: %w" , err )
215
+ }
216
+
217
+ return content , nil
125
218
}
126
219
127
- func fetchEC2ClusterNameTag ( awsConfig awssdk. Config , instanceID string ) ( string , error ) {
128
- svc := NewEC2Client ( awsConfig )
220
+ // clusterNameFromDescribeTag is a helper to extract EKS cluster name using DescribeTag.
221
+ func clusterNameFromDescribeTag ( ctx context. Context , ec2Client EC2Client , instanceID string ) ( string , error ) {
129
222
input := & ec2.DescribeTagsInput {
130
223
Filters : []types.Filter {
131
224
{
@@ -135,15 +228,13 @@ func fetchEC2ClusterNameTag(awsConfig awssdk.Config, instanceID string) (string,
135
228
},
136
229
},
137
230
{
138
- Name : awssdk .String ("key" ),
139
- Values : []string {
140
- "eks:cluster-name" ,
141
- },
231
+ Name : awssdk .String ("key" ),
232
+ Values : []string {eksClusterNameTagKey },
142
233
},
143
234
},
144
235
}
145
236
146
- tagsResult , err := svc .DescribeTags (context . TODO () , input )
237
+ tagsResult , err := ec2Client .DescribeTags (ctx , input )
147
238
if err != nil {
148
239
return "" , fmt .Errorf ("error fetching EC2 Tags: %w" , err )
149
240
}
0 commit comments