package io.fathom.cloud.compute.services;
import io.fathom.cloud.compute.scheduler.SchedulerHost;
import io.fathom.cloud.protobuf.CloudModel.HostData;
import io.fathom.cloud.protobuf.CloudModel.HostGroupData;
import io.fathom.cloud.protobuf.CloudModel.HostGroupSecretData;
import io.fathom.cloud.protobuf.CloudModel.HostGroupType;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.util.Date;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.internal.EC2MetadataClient;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.util.DateUtils;
import com.amazonaws.util.json.JSONException;
import com.amazonaws.util.json.JSONObject;
import com.google.common.base.Strings;
public class Ec2DatacenterManager implements DatacenterManager {
final HostGroupData hostGroupData;
AmazonEC2Client ec2Client;
public Ec2DatacenterManager(HostGroupData hostGroupData, HostGroupSecretData secretData) {
this.hostGroupData = hostGroupData;
if (hostGroupData.getHostGroupType() != HostGroupType.HOST_GROUP_TYPE_AMAZON_EC2) {
throw new IllegalStateException();
}
if (secretData.hasUsername()) {
String accessKey = secretData.getUsername();
String secretKey = secretData.getPassword();
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
ec2Client = new AmazonEC2Client(awsCredentials);
}
}
public AmazonEC2Client getEc2Client(SchedulerHost host) {
if (this.ec2Client != null) {
return this.ec2Client;
}
CredentialsProvider credentialsProvider = new CredentialsProvider(host);
AmazonEC2Client ec2Client = new AmazonEC2Client(credentialsProvider);
return ec2Client;
}
/**
* Borrowed from AWS SDK (Apache licensed)
* InstanceProfileCredentialsProvider
*/
class CredentialsProvider extends InstanceProfileCredentialsProvider {
private final SchedulerHost host;
protected volatile AWSCredentials credentials;
protected volatile Date credentialsExpiration;
public CredentialsProvider(SchedulerHost host) {
this.host = host;
}
@Override
public AWSCredentials getCredentials() {
if (needsToLoadCredentials()) {
loadCredentials();
}
if (expired()) {
throw new AmazonClientException(
"The credentials received from the Amazon EC2 metadata service have expired");
}
return credentials;
}
private boolean expired() {
if (credentialsExpiration != null) {
if (credentialsExpiration.getTime() < System.currentTimeMillis()) {
return true;
}
}
return false;
}
private synchronized void loadCredentials() {
if (needsToLoadCredentials()) {
try {
// String credentialsResponse = new
// EC2MetadataClient().getDefaultCredentials();
String credentialsResponse = getDefaultCredentials();
JSONObject jsonObject = new JSONObject(credentialsResponse);
if (jsonObject.has("Token")) {
credentials = new BasicSessionCredentials(jsonObject.getString("AccessKeyId"),
jsonObject.getString("SecretAccessKey"), jsonObject.getString("Token"));
} else {
credentials = new BasicAWSCredentials(jsonObject.getString("AccessKeyId"),
jsonObject.getString("SecretAccessKey"));
}
if (jsonObject.has("Expiration")) {
/*
* TODO: The expiration string comes in a different
* format than what we deal with in other parts of the
* SDK, so we have to convert it to the ISO8601 syntax
* we expect.
*/
String expiration = jsonObject.getString("Expiration");
expiration = expiration.replaceAll("\\+0000$", "Z");
credentialsExpiration = new DateUtils().parseIso8601Date(expiration);
}
} catch (IOException e) {
throw new AmazonClientException("Unable to load credentials from Amazon EC2 metadata service", e);
} catch (JSONException e) {
throw new AmazonClientException("Unable to parse credentials from Amazon EC2 metadata service", e);
} catch (ParseException e) {
throw new AmazonClientException(
"Unable to parse credentials expiration date from Amazon EC2 metadata service", e);
}
}
}
private String getDefaultCredentials() throws IOException {
String securityCredentialsList = readResource(EC2MetadataClient.SECURITY_CREDENTIALS_RESOURCE);
securityCredentialsList = securityCredentialsList.trim();
String[] securityCredentials = securityCredentialsList.split("\n");
if (securityCredentials.length == 0) {
return null;
}
String securityCredentialsName = securityCredentials[0];
return readResource(EC2MetadataClient.SECURITY_CREDENTIALS_RESOURCE + securityCredentialsName);
}
private String readResource(String resourcePath) throws IOException {
String url = "http://169.254.169.254" + resourcePath;
URI uri = URI.create(url);
return host.fetchUrl(uri);
}
}
public String findHost(SchedulerHost host) {
HostData hostData = host.getHostData();
if (hostData.getHostGroup() != hostGroupData.getId()) {
throw new IllegalStateException();
}
String providerId = hostData.getProviderId();
if (Strings.isNullOrEmpty(providerId)) {
throw new IllegalStateException();
}
return providerId;
}
}