Skip to content

Commit 76f1f4f

Browse files
authored
Merge pull request #24 from Himtanaya/migrate-imdsv1-to-imdsv2
Migrate from IMDSv1 to IMDSv2
2 parents 7c98b53 + 91068f6 commit 76f1f4f

File tree

7 files changed

+97
-64
lines changed

7 files changed

+97
-64
lines changed

src/Amazon.CloudWatch.EMF/Amazon.CloudWatch.EMF.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<PackageId>Amazon.CloudWatch.EMF</PackageId>
5-
<VersionPrefix>0.1.0</VersionPrefix>
5+
<VersionPrefix>0.1.1</VersionPrefix>
66
<Authors>Amazon Web Services</Authors>
77
<Description>Amazon CloudWatch Embedded Metric Format Client Library</Description>
88
<Language>en-US</Language>

src/Amazon.CloudWatch.EMF/Environment/EC2Environment.cs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using Amazon.CloudWatch.EMF.Config;
34
using Amazon.CloudWatch.EMF.Model;
45
using Microsoft.Extensions.Logging;
@@ -9,11 +10,18 @@ namespace Amazon.CloudWatch.EMF.Environment
910
{
1011
public class EC2Environment : AgentBasedEnvironment
1112
{
13+
// Documentation for configuring instance metadata can be found here:
14+
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
1215
private const string INSTANCE_IDENTITY_URL = "http://169.254.169.254/latest/dynamic/instance-identity/document";
16+
private const string TOKEN_URL = "http://169.254.169.254/latest/api/token";
17+
private const string TOKEN_REQUEST_HEADER_KEY = "X-aws-ec2-metadata-token-ttl-seconds";
18+
private const string TOKEN_REQUEST_HEADER_VALUE = "21600";
19+
private const string METADATA_REQUEST_HEADER_KEY = "X-aws-ec2-metadata-token";
1320
private const string CFN_EC2_TYPE = "AWS::EC2::Instance";
1421

1522
private readonly ILogger _logger;
1623
private readonly IResourceFetcher _resourceFetcher;
24+
private string _token;
1725
private EC2Metadata _ec2Metadata;
1826

1927
public EC2Environment(IConfiguration configuration, IResourceFetcher resourceFetcher)
@@ -32,10 +40,35 @@ public EC2Environment(IConfiguration configuration, IResourceFetcher resourceFet
3240

3341
public override bool Probe()
3442
{
35-
Uri uri = null;
43+
Uri tokenUri = null;
44+
var tokenRequestHeader = new Dictionary<string, string>();
45+
tokenRequestHeader.Add(TOKEN_REQUEST_HEADER_KEY, TOKEN_REQUEST_HEADER_VALUE);
3646
try
3747
{
38-
uri = new Uri(INSTANCE_IDENTITY_URL);
48+
tokenUri = new Uri(TOKEN_URL);
49+
}
50+
catch (Exception)
51+
{
52+
_logger.LogDebug("Failed to construct url: " + TOKEN_URL);
53+
return false;
54+
}
55+
56+
try
57+
{
58+
_token = _resourceFetcher.FetchString(tokenUri, "PUT", tokenRequestHeader);
59+
}
60+
catch (EMFClientException ex)
61+
{
62+
_logger.LogDebug("Failed to get response from: " + tokenUri, ex);
63+
return false;
64+
}
65+
66+
Uri metadataUri = null;
67+
var metadataRequestHeader = new Dictionary<string, string>();
68+
metadataRequestHeader.Add(METADATA_REQUEST_HEADER_KEY, _token);
69+
try
70+
{
71+
metadataUri = new Uri(INSTANCE_IDENTITY_URL);
3972
}
4073
catch (Exception)
4174
{
@@ -45,12 +78,12 @@ public override bool Probe()
4578

4679
try
4780
{
48-
_ec2Metadata = _resourceFetcher.Fetch<EC2Metadata>(uri);
81+
_ec2Metadata = _resourceFetcher.FetchJson<EC2Metadata>(metadataUri, "GET", metadataRequestHeader);
4982
return true;
5083
}
5184
catch (EMFClientException ex)
5285
{
53-
_logger.LogDebug("Failed to get response from: " + uri, ex);
86+
_logger.LogDebug("Failed to get response from: " + metadataUri, ex);
5487
}
5588

5689
return false;

src/Amazon.CloudWatch.EMF/Environment/ECSEnvironment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public override bool Probe()
5252
try
5353
{
5454
var parsedUri = new Uri(uri);
55-
_ecsMetadata = _resourceFetcher.Fetch<ECSMetadata>(parsedUri);
55+
_ecsMetadata = _resourceFetcher.FetchJson<ECSMetadata>(parsedUri, "GET");
5656
FormatImageName();
5757
return true;
5858
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
24

35
namespace Amazon.CloudWatch.EMF.Environment
46
{
57
public interface IResourceFetcher
68
{
7-
public T Fetch<T>(Uri endpoint);
9+
public T FetchJson<T>(Uri endpoint, string method, Dictionary<string, string> header = null);
10+
11+
public string FetchString(Uri endpoint, string method, Dictionary<string, string> header = null);
812
}
913
}
Lines changed: 36 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2-
using System.IO;
2+
using System.Collections.Generic;
33
using System.Net;
4+
using System.Net.Http;
5+
using System.Threading.Tasks;
46
using Microsoft.Extensions.Logging;
57
using Microsoft.Extensions.Logging.Abstractions;
68
using Newtonsoft.Json;
@@ -25,33 +27,44 @@ public ResourceFetcher(ILoggerFactory loggerFactory)
2527
/// Fetch a json object from a given uri and deserialize it to the specified class: T
2628
/// </summary>
2729
/// <returns></returns>
28-
public T Fetch<T>(Uri endpoint)
30+
public T FetchJson<T>(Uri endpoint, string method, Dictionary<string, string> header = null)
2931
{
30-
string response = ReadResource(endpoint, "GET");
32+
string response = ReadResource(endpoint, method, header).Result;
3133

3234
return JsonConvert.DeserializeObject<T>(response);
3335
}
3436

35-
private string ReadResource(Uri endpoint, string method)
37+
/// <summary>
38+
/// Fetch string from a given uri
39+
/// </summary>
40+
/// <returns></returns>
41+
public string FetchString(Uri endpoint, string method, Dictionary<string, string> headers = null)
42+
{
43+
string response = ReadResource(endpoint, method, headers).Result;
44+
45+
return response;
46+
}
47+
48+
private async Task<string> ReadResource(Uri endpoint, string method, Dictionary<string, string> headers)
3649
{
3750
try
3851
{
39-
var httpWebRequest = GetHttpWebRequest(endpoint, method);
52+
headers ??= new Dictionary<string, string>();
4053

41-
var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
54+
var httpResponse = GetResponse(endpoint, method, headers);
4255

43-
if (httpWebResponse.StatusCode == HttpStatusCode.OK)
44-
{
45-
return GetResponse(httpWebResponse);
46-
}
47-
else if (httpWebResponse.StatusCode == HttpStatusCode.NotFound)
56+
if (httpResponse.StatusCode == HttpStatusCode.OK)
4857
{
49-
throw new EMFClientException("The requested metadata is not found at " + httpWebRequest.RequestUri.AbsolutePath);
58+
return await httpResponse.Content.ReadAsStringAsync();
5059
}
51-
else
60+
61+
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
5262
{
53-
HandleErrorResponse(httpWebResponse);
63+
throw new EMFClientException("The requested data is not found at " + endpoint.AbsolutePath);
5464
}
65+
66+
throw new EMFClientException("Failed to get resource. Error code: " + httpResponse.StatusCode +
67+
", error message: " + httpResponse.ReasonPhrase);
5568
}
5669
catch (Exception e)
5770
{
@@ -61,52 +74,22 @@ private string ReadResource(Uri endpoint, string method)
6174
+ "\n Attempting to reconnect.");
6275
throw new EMFClientException("Failed to connect to service endpoint: ", e);
6376
}
64-
65-
return string.Empty;
6677
}
6778

68-
private void HandleErrorResponse(HttpWebResponse httpWebResponse)
79+
private HttpResponseMessage GetResponse(Uri endpoint, string method, Dictionary<string, string> headers)
6980
{
70-
string errorResponse = GetResponse(httpWebResponse);
81+
HttpClient client = new HttpClient();
7182

72-
try
73-
{
74-
/*JsonNode node = Jackson.jsonNodeOf(errorResponse);
75-
JsonNode code = node.get("code");
76-
JsonNode message = node.get("message");
77-
if (code != null && message != null) {
78-
errorCode = code.asText();
79-
responseMessage = message.asText();
80-
}
81-
82-
String exceptionMessage =
83-
String.format(
84-
"Failed to get resource. Error code: %s, error message: %s ",
85-
errorCode, responseMessage);
86-
throw new EMFClientException(exceptionMessage);*/
87-
}
88-
catch (System.Exception exception)
83+
var httpMethod = new HttpMethod(method.ToUpper());
84+
HttpRequestMessage request = new HttpRequestMessage(httpMethod, endpoint);
85+
foreach (KeyValuePair<string, string> header in headers)
8986
{
90-
throw new EMFClientException("Unable to parse error stream: ", exception);
87+
request.Headers.Add(header.Key, header.Value);
9188
}
92-
}
93-
94-
private HttpWebRequest GetHttpWebRequest(Uri endpoint, string method)
95-
{
96-
var httpWebRequest = (HttpWebRequest)WebRequest.CreateHttp(endpoint);
97-
httpWebRequest.Method = method;
98-
httpWebRequest.Timeout = 1000;
99-
httpWebRequest.ReadWriteTimeout = 1000;
100-
return httpWebRequest;
101-
}
102-
103-
private string GetResponse(HttpWebResponse response)
104-
{
105-
var inputStream = response.GetResponseStream();
10689

107-
// convert stream to string
108-
using var reader = new StreamReader(inputStream);
109-
return reader.ReadToEnd();
90+
Task<HttpResponseMessage> response = client.SendAsync(request);
91+
HttpResponseMessage result = response.Result;
92+
return result;
11093
}
11194
}
11295
}

tests/Amazon.CloudWatch.EMF.Tests/Environment/EC2EnvironmentTests.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using Amazon.CloudWatch.EMF.Config;
34
using Amazon.CloudWatch.EMF.Environment;
45
using AutoFixture;
@@ -167,7 +168,9 @@ public void Probe_False()
167168
// Arrange
168169
var configuration = _fixture.Create<IConfiguration>();
169170
var resourceFetcher = _fixture.Create<IResourceFetcher>();
170-
resourceFetcher.Fetch<EC2Metadata>(Arg.Any<Uri>()).Throws<EMFClientException>();
171+
resourceFetcher.FetchString(
172+
Arg.Any<Uri>(), Arg.Any<string>(), Arg.Any<Dictionary<string, string>>()
173+
).Throws<EMFClientException>();
171174
var environment = new EC2Environment(configuration, resourceFetcher);
172175

173176
// Act
@@ -183,7 +186,12 @@ public void Type_WhenNoMetadata()
183186
// Arrange
184187
var configuration = _fixture.Create<IConfiguration>();
185188
var resourceFetcher = _fixture.Create<IResourceFetcher>();
186-
resourceFetcher.Fetch<EC2Metadata>(Arg.Any<Uri>()).Throws<EMFClientException>();
189+
resourceFetcher.FetchString(
190+
Arg.Any<Uri>(), Arg.Any<string>(), Arg.Any<Dictionary<string, string>>()
191+
).Returns("fake_token");
192+
resourceFetcher.FetchJson<EC2Metadata>(
193+
Arg.Any<Uri>(), Arg.Any<string>(), Arg.Any<Dictionary<string, string>>()
194+
).Throws<EMFClientException>();
187195
var environment = new EC2Environment(configuration, resourceFetcher);
188196
environment.Probe();
189197

@@ -199,7 +207,12 @@ public void Type_WithMetadata()
199207
// Arrange
200208
var configuration = _fixture.Create<IConfiguration>();
201209
var resourceFetcher = _fixture.Create<IResourceFetcher>();
202-
resourceFetcher.Fetch<EC2Metadata>(Arg.Any<Uri>()).Returns(new EC2Metadata());
210+
resourceFetcher.FetchString(
211+
Arg.Any<Uri>(), Arg.Any<string>(), Arg.Any<Dictionary<string, string>>()
212+
).Returns("fake_token");
213+
resourceFetcher.FetchJson<EC2Metadata>(
214+
Arg.Any<Uri>(), Arg.Any<string>(), Arg.Any<Dictionary<string, string>>()
215+
).Returns(new EC2Metadata());
203216
var environment = new EC2Environment(configuration, resourceFetcher);
204217
environment.Probe();
205218

tests/Amazon.CloudWatch.EMF.Tests/Environment/ECSEnvironmentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void Probe_False()
152152
// Arrange
153153
var configuration = _fixture.Create<IConfiguration>();
154154
var resourceFetcher = _fixture.Create<IResourceFetcher>();
155-
resourceFetcher.Fetch<ECSMetadata>(Arg.Any<Uri>()).Throws<EMFClientException>();
155+
resourceFetcher.FetchJson<ECSMetadata>(Arg.Any<Uri>(), Arg.Any<string>()).Throws<EMFClientException>();
156156
var environment = new ECSEnvironment(configuration, resourceFetcher);
157157

158158
// Act

0 commit comments

Comments
 (0)