package denominator.hook;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import static denominator.common.Preconditions.checkArgument;
import static denominator.common.Preconditions.checkNotNull;
import static denominator.common.Util.slurp;
import static denominator.common.Util.split;
/**
* Utilities used for accessing metadata when running on a EC2 instance or otherwise that can access
* {@code http://169.254.169.254/latest/meta-data/}.
*
* See <a href= "http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html"
* > documentation</a>
*/
public class InstanceMetadataHook {
/**
* location of the metadata service
*/
public static final URI DEFAULT_URI = URI.create("http://169.254.169.254/latest/meta-data/");
/**
* Retrieves a list of resources at {@code http://169.254.169.254/latest/meta-data/PATH}, if
* present.
*
* @param path path of a listable resource, such as {@code iam/security-credentials/}; must end in
* slash
* @return empty if {@code metadataService} service cannot be contacted or no data at path.
*/
public static List<String> list(String path) {
return list(DEFAULT_URI, path);
}
/**
* Retrieves a list of resources at a path, if present.
*
* @param metadataService endpoint with trailing slash. ex. {@code http://169.254.169.254/latest/meta-data/}
* @param path path of a listable resource, such as {@code iam/security-credentials/};
* must end in slash
* @return empty if {@code metadataService} service cannot be contacted or no data at path.
*/
public static List<String> list(URI metadataService, String path) {
checkArgument(checkNotNull(path, "path").endsWith("/"), "path must end with '/'; %s provided",
path);
String content = get(metadataService, path);
if (content != null) {
return split('\n', content);
}
return Collections.<String>emptyList();
}
/**
* Retrieves a resources at {@code http://169.254.169.254/latest/meta-data/PATH}, if present.
*
* @param path path to the metadata desired. ex. {@code public-ipv4} or {@code
* iam/security-credentials/role-name}
* @return null if {@code metadataService} service cannot be contacted or no data at path.
*/
public static String get(String path) {
return get(DEFAULT_URI, path);
}
/**
* Retrieves content at a path, if present.
*
* @param metadataService endpoint with trailing slash. ex. {@code http://169.254.169.254/latest/meta-data/}
* @param path path to the metadata desired. ex. {@code public-ipv4} or {@code
* iam/security-credentials/role-name}
* @return null if {@code metadataService} service cannot be contacted or no data at path.
*/
public static String get(URI metadataService, String path) {
checkNotNull(metadataService, "metadataService");
checkArgument(metadataService.getPath().endsWith("/"),
"metadataService must end with '/'; %s provided",
metadataService);
checkNotNull(path, "path");
InputStream stream = null;
try {
stream = openStream(metadataService + path);
String content = slurp(new InputStreamReader(stream));
if (content.isEmpty()) {
return null;
}
return content;
} catch (IOException e) {
return null;
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
}
}
}
/**
* quick http client that allows no-dependency try at getting instance data.
*/
private static InputStream openStream(String resource) throws IOException {
HttpURLConnection
connection =
HttpURLConnection.class.cast(URI.create(resource).toURL().openConnection());
connection.setConnectTimeout(1000 * 2);
connection.setReadTimeout(1000 * 2);
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(false);
return connection.getInputStream();
}
}