//-----------------------------------------------------------------------------
//
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using Amazon.Runtime.Internal.Util;
using ThirdParty.LitJson;
namespace Amazon.XRay.Recorder.Core.Plugins
{
///
/// This is a plugin for EC2.
///
public class EC2Plugin : IPlugin
{
private static readonly Logger _logger = Logger.GetLogger(typeof(EC2Plugin));
const string metadata_base_url = "http://169.254.169.254/latest/";
///
/// Gets the name of the origin associated with this plugin.
///
/// The name of the origin associated with this plugin.
public string Origin
{
get
{
return @"AWS::EC2::Instance";
}
}
///
/// Gets the name of the service associated with this plugin.
///
/// The name of the service that this plugin is associated with.
public string ServiceName
{
get
{
return @"ec2";
}
}
///
/// Gets the context of the runtime that this plugin is associated with.
///
/// When the method returns, contains the runtime context of the plugin.
/// true if the runtime context is available; Otherwise, false.
public bool TryGetRuntimeContext(out IDictionary context)
{
// get the token
string token = GetToken();
// get the metadata
context = GetMetadata(token);
if (context.Count == 0)
{
_logger.DebugFormat("Could not get instance metadata");
return false;
}
return true;
}
private string GetToken()
{
string token = null;
try
{
Dictionary header = new Dictionary(1);
header.Add("X-aws-ec2-metadata-token-ttl-seconds", "60");
token = DoRequest(metadata_base_url + "api/token", HttpMethod.Put, header);
}
catch (Exception)
{
_logger.DebugFormat("Failed to get token for IMDSv2");
}
return token;
}
private IDictionary GetMetadata(string token)
{
try
{
Dictionary headers = null;
if (token != null)
{
headers = new Dictionary(1);
headers.Add("X-aws-ec2-metadata-token", token);
}
string identity_doc_url = metadata_base_url + "dynamic/instance-identity/document";
string doc_string = DoRequest(identity_doc_url, HttpMethod.Get, headers);
return EC2Plugin.ParseMetadata(doc_string);
}
catch (Exception)
{
_logger.DebugFormat("Error occurred while getting EC2 metadata");
return new Dictionary();
}
}
protected virtual string DoRequest(string url, HttpMethod method, Dictionary headers = null)
{
var httpWebRequest = WebRequest.CreateHttp(url);
httpWebRequest.Timeout = 2000; // 2 seconds timeout
httpWebRequest.Method = method.Method;
if (headers != null)
{
foreach (var item in headers)
{
httpWebRequest.Headers.Add(item.Key, item.Value);
}
}
using(var response = (HttpWebResponse)httpWebRequest.GetResponse())
{
if (response.StatusCode < HttpStatusCode.OK || (int)response.StatusCode > 299)
{
throw new Exception("Unable to complete the request successfully");
}
var encoding = !string.IsNullOrEmpty(response.ContentEncoding)
? Encoding.GetEncoding(response.ContentEncoding)
: Encoding.UTF8;
using (var streamReader = new StreamReader(response.GetResponseStream(), encoding))
{
return streamReader.ReadToEnd();
}
}
}
private static IDictionary ParseMetadata(string jsonString)
{
JsonData data = JsonMapper.ToObject(jsonString);
Dictionary ec2_meta_dict = new Dictionary();
ec2_meta_dict.Add("instance_id", data["instanceId"]);
ec2_meta_dict.Add("availability_zone", data["availabilityZone"]);
ec2_meta_dict.Add("instance_size", data["instanceType"]);
ec2_meta_dict.Add("ami_id", data["imageId"]);
return ec2_meta_dict;
}
}
}