/*
* Copyright 2012 Nodeable Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.
*/
package com.streamreduce.util;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import com.streamreduce.ProviderIdConstants;
import com.streamreduce.core.model.*;
import com.streamreduce.core.service.exception.InvalidCredentialsException;
import net.sf.json.*;
import net.sf.json.xml.XMLSerializer;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.jclouds.ContextBuilder;
import org.jclouds.aws.ec2.AWSEC2ApiMetadata;
import org.jclouds.aws.ec2.AWSEC2Client;
import org.jclouds.aws.ec2.domain.Tag;
import org.jclouds.aws.ec2.reference.AWSEC2Constants;
import org.jclouds.aws.ec2.util.TagFilters;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.cloudwatch.CloudWatchAsyncApi;
import org.jclouds.cloudwatch.CloudWatchApi;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.RestContext;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Sets.filter;
import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
import static org.jclouds.compute.predicates.NodePredicates.all;
/**
* AWSClient provides necessary methods for interacting with Amazon Web Services.
*/
public class AWSClient extends ExternalIntegrationClient {
private static final String BUCKET_PREFIX = "com.streamreduce";
private static final String S3_OBJECT_EVENT_PREFIX = "event/";
private static final String S3_OBJECT_SOBA_MESSAGE_PREFIX = "message/";
private BlobStoreContext blobStoreContext;
private ComputeServiceContext computeServiceContext;
private RestContext<CloudWatchApi, CloudWatchAsyncApi> cloudWatchContext;
private static final String EC2_API_BASE = "ec2.amazonaws.com";
public AWSClient(Connection connection) {
super(connection);
Assert.isTrue(connection.getProviderId().equals(ProviderIdConstants.AWS_PROVIDER_ID));
}
public AWSClient(OutboundConfiguration outboundConfiguration) {
super(outboundConfiguration);
Assert.isTrue(outboundConfiguration.getProtocol().equals("s3"));
}
/**
* Destroys a compute instance and returns the result.
*
* @param nodeId the node id to destroy
*
* @return the success of the destroy attempt
*
* @throws InvalidCredentialsException if the connection's credentials are invalid
*/
public boolean destroyEC2Instance(String nodeId) throws InvalidCredentialsException {
getComputeServiceContext().getComputeService().destroyNode(nodeId);
NodeMetadata nodeMetadata = getEC2Instance(nodeId);
return new RetryablePredicate<>(NodePredicates.RUNNING, 30, 5,
TimeUnit.SECONDS).apply(nodeMetadata);
}
/**
* Reboots the instance and returns the result.
*
* @param nodeId the node id to reboot
*
* @return the success of the reboot attempt
*
* @throws InvalidCredentialsException if the connection's credentials are invalid
*/
public boolean rebootEC2Instance(String nodeId) throws InvalidCredentialsException {
getComputeServiceContext().getComputeService().rebootNode(nodeId);
NodeMetadata nodeMetadata = getEC2Instance(nodeId);
return new RetryablePredicate<>(NodePredicates.RUNNING, 30, 5,
TimeUnit.SECONDS).apply(nodeMetadata);
}
/**
* Returns the node metadata for the given node id.
*
* @param nodeId the jclouds node id
*
* @return the node metadata
*
* @throws InvalidCredentialsException if the connection's credentials are invalid
*/
public NodeMetadata getEC2Instance(String nodeId) throws InvalidCredentialsException {
return getComputeServiceContext().getComputeService().getNodeMetadata(nodeId);
}
/**
* Return the {@link Tag}s currently applied to the EC2 instance identified by the node id passed in.
*
* @param nodeId the node id of the EC2 instance we're interested in
*
* @return set of tag objects.
*
* @throws InvalidCredentialsException if the connection's credentials are invalid
*/
public Set<Tag> getEC2InstanceTags(String nodeId) throws InvalidCredentialsException {
AWSEC2Client ec2Client = AWSEC2Client.class.cast(getComputeServiceContext()
.unwrap(AWSEC2ApiMetadata.CONTEXT_TOKEN).getApi());
return ec2Client.getTagServices()
.describeTagsInRegion(null, TagFilters.filters()
.resourceType(TagFilters.ResourceType.INSTANCE)
.resourceId(nodeId).build());
}
/**
* Returns a list of JSONObjects representing the compute nodes available to this AWS connection.
*
* @return list of JSONObjects representing the compute nodes
*
* @throws InvalidCredentialsException if the client's credentials are invalid
*/
public List<JSONObject> getEC2Instances() throws InvalidCredentialsException {
ComputeService computeService = getComputeServiceContext().getComputeService();
Set<? extends NodeMetadata> rawComputeNodes = filter(
computeService.listNodesDetailsMatching(all()), not(TERMINATED));
List<JSONObject> computeNodes = new ArrayList<>();
for (NodeMetadata computeNode : rawComputeNodes) {
computeNodes.add(JSONObject.fromObject(computeNode));
}
return computeNodes;
}
public Set<? extends StorageMetadata> getS3Buckets() throws InvalidCredentialsException {
BlobStore store = getBlobStoreContext().getBlobStore();
return store.list();
}
/**
* Returns a list of JSONObjects representing the storage blobs available to this AWS connection.
*
* @return list of JSONObjects representing the storage blobs
*
* @throws InvalidCredentialsException if the client's credentials are invalid
*/
public List<JSONObject> getS3BucketsAsJson() throws InvalidCredentialsException {
Set<? extends StorageMetadata> rawBlobs = getS3Buckets();
List<JSONObject> blobs = new ArrayList<>();
for (StorageMetadata storageMetadata : rawBlobs) {
blobs.add(JSONObject.fromObject(storageMetadata));
}
return blobs;
}
public List<JSONObject> describeRegions() throws IOException, InvalidCredentialsException {
Map<String, String> params = new HashMap<>();
params.put("Action", "DescribeRegions");
params.put("Version", "2012-08-15");
JSONObject rawResponse = makeRequest(EC2_API_BASE, params);
return getJSONChildren("regionInfo", rawResponse);
}
public List<JSONObject> describeImages(String endpoint, Set<String> imageIds) throws IOException, InvalidCredentialsException {
Map<String, String> params = new HashMap<>();
params.put("Action", "DescribeImages");
params.put("Version", "2012-08-15");
int count = 0;
for (String imageId : imageIds) {
count += 1;
params.put("ImageId." + count, imageId);
}
JSONObject rawResponse = makeRequest(endpoint, params);
return getJSONChildren("item", rawResponse.getJSONObject("imagesSet"));
}
public List<JSONObject> describeInstances(String endpoint) throws IOException, InvalidCredentialsException {
List<JSONObject> response = new ArrayList<>();
HashMap<String, String> params = new HashMap<>();
params.put("Action", "DescribeInstances");
params.put("Version", "2012-08-15");
params.put("Filter.1.Name", "instance-state-name");
params.put("Filter.1.Value.1", "running");
params.put("Filter.1.Value.2", "stopped");
JSONObject rawResponse = makeRequest(endpoint, params);
for (JSONObject reservation : getJSONChildren("item", rawResponse.getJSONObject("reservationSet"))) {
response.addAll(getJSONChildren("item", reservation.getJSONObject("instancesSet")));
}
return response;
}
public List<JSONObject> describeInstanceStatus(String endpoint) throws IOException, InvalidCredentialsException {
List<JSONObject> response = new ArrayList<>();
HashMap<String, String> params = new HashMap<>();
String nextToken = null;
do {
params.put("Action", "DescribeInstanceStatus");
params.put("Version", "2012-08-15");
if (nextToken != null) {
params.put("NextToken", nextToken);
}
JSONObject rawResponse = makeRequest(endpoint, params);
response.addAll(getJSONChildren("item", rawResponse.getJSONObject("instanceStatusSet")));
Object obj = rawResponse.get("NextToken");
nextToken = (String) obj;
} while (nextToken != null);
return response;
}
public List<JSONObject> listMetrics(String endpoint) throws IOException, InvalidCredentialsException {
List<JSONObject> response = new ArrayList<>();
String nextToken = null;
do {
HashMap<String, String> params = new HashMap<>();
params.put("Action", "ListMetrics");
params.put("Version", "2010-08-01");
if (nextToken != null) {
params.put("NextToken", nextToken);
}
JSONObject rawResponse = makeRequest(endpoint, params);
response.addAll(getJSONChildren("member", rawResponse.getJSONObject("ListMetricsResult").getJSONObject("Metrics")));
Object obj = rawResponse.getJSONObject("ListMetricsResult").get("NextToken");
nextToken = (String) obj;
} while (nextToken != null);
return response;
}
public List<JSONObject> getMetricStatistics(String endpoint, String namespace, String metricName, String dimensionName, String dimensionValue)
throws IOException, InvalidCredentialsException {
Map<String, String> params = new HashMap<>();
params.put("Action", "GetMetricStatistics");
params.put("Version", "2010-08-01");
params.put("Namespace", namespace);
params.put("MetricName", metricName);
params.put("Dimensions.member.1.Name", dimensionName);
params.put("Dimensions.member.1.Value", dimensionValue);
params.put("Statistics.member.1", "Average");
long now = new Date().getTime();
params.put("StartTime", getFormattedTimestamp(new Date(now - 60 * 60 * 1000)));
params.put("EndTime", getFormattedTimestamp(new Date(now)));
params.put("Period", "60");
JSONObject rawResponse = makeRequest(endpoint, params);
return getJSONChildren("member", rawResponse.getJSONObject("GetMetricStatisticsResult").getJSONObject("Datapoints"));
}
public Map<String, JSONArray> describeInstanceAndImageForRunningAndStoppedInstances(String endpoint) throws IOException, InvalidCredentialsException {
Map<String, JSONArray> instanceAndImageByInstanceId = new HashMap<>();
Map<String, JSONObject> instancesById = new HashMap<>();
Map<String, JSONObject> imagesById = new HashMap<>();
List<JSONObject> instances = describeInstances(endpoint);
for (JSONObject instance : instances) {
String instanceId = instance.getString("instanceId");
String imageId = instance.getString("imageId");
instancesById.put(instanceId, instance);
imagesById.put(imageId, null);
}
List<JSONObject> images = describeImages(endpoint, imagesById.keySet());
for (JSONObject image : images) {
String imageId = image.getString("imageId");
imagesById.put(imageId, image);
}
for (JSONObject instance : instances) {
String instanceId = instance.getString("instanceId");
String imageId = instance.getString("imageId");
JSONArray arr = new JSONArray();
arr.element(instancesById.get(instanceId));
arr.element(imagesById.get(imageId));
instanceAndImageByInstanceId.put(instanceId, arr);
}
return instanceAndImageByInstanceId;
}
public Map<String, JSONObject> getMetricsStatisticsForRunningInstances(String endpoint) throws IOException, InvalidCredentialsException {
Map<String, JSONObject> statisticsByInstance = new HashMap<>();
List<JSONObject> instances = describeInstances(endpoint);
List<JSONObject> metrics = listMetrics(endpoint.replace("ec2", "monitoring"));
List<JSONObject> runningInstances = filterInstancesByInstanceState("running", instances);
for (JSONObject instance : runningInstances) {
String instanceId = instance.getString("instanceId");
List<JSONObject> instanceMetrics = filterMetricsByInstanceId(instanceId, metrics);
JSONObject statistics = new JSONObject();
for (JSONObject metric : instanceMetrics) {
List<JSONObject> datapoints =
getMetricStatistics(endpoint.replace("ec2", "monitoring"),
metric.getString("Namespace"), metric.getString("MetricName"), "InstanceId", instanceId);
statistics.element(metric.getString("MetricName"), datapoints.get(0));
}
statisticsByInstance.put(instanceId, statistics);
}
return statisticsByInstance;
}
private List<JSONObject> filterInstancesByInstanceState(String instanceState, List<JSONObject> instances) {
List<JSONObject> filtered = new ArrayList<>();
for (JSONObject instance : instances) {
String stateName = instance.getJSONObject("instanceState").getString("name");
if (stateName.equals(instanceState)) {
filtered.add(instance);
}
}
return filtered;
}
private List<JSONObject> filterMetricsByInstanceId(String instanceId, List<JSONObject> metrics) {
List<JSONObject> filtered = new ArrayList<>();
for (JSONObject metric : metrics) {
JSONObject dimension = metric.getJSONObject("Dimensions").getJSONObject("member");
String dimensionName = dimension.getString("Name");
String dimensionValue = dimension.getString("Value");
if (dimensionName.equals("InstanceId") && dimensionValue.equals(instanceId)) {
filtered.add(metric);
}
}
return filtered;
}
public List<JSONObject> getService(String endpoint) throws IOException, InvalidCredentialsException {
JSONObject rawResponse = makeS3Request(endpoint, null, "/");
return getJSONChildren("Bucket", rawResponse.getJSONObject("Buckets"));
}
public JSONObject getBucketLocation(String endpoint, String bucket) throws IOException, InvalidCredentialsException {
JSONObject rawResponse = makeS3Request(endpoint, bucket, "/?location");
return new JSONObject().element("LocationConstraint", rawResponse == null ? "US" : rawResponse.get("#text"));
}
public List<JSONObject> getBucketsWithLocation(String endpoint) throws IOException, InvalidCredentialsException {
List<JSONObject> buckets = getService("s3.amazonaws.com");
for (JSONObject bucket : buckets) {
JSONObject location = getBucketLocation("s3.amazonaws.com", bucket.getString("Name"));
bucket.element("LocationConstraint", location.get("LocationConstraint"));
}
return buckets;
}
private JSONObject makeRequest(String endpoint, Map<String, String> params) throws IOException, InvalidCredentialsException {
String url = signUrl("https://" + endpoint + "/", params);
String rawResponse = HTTPUtils.openUrl(url, "GET", null, null, null, null, null, null);
JSONObject json = (JSONObject) new XMLSerializer().read(rawResponse);
return json;
}
private JSONObject makeS3Request(String endpoint, String bucket, String path) throws IOException, InvalidCredentialsException {
String date = getS3FormattedTimestamp(new Date());
String signature = calculateS3Signature((bucket == null? "" : "/" + bucket) + path, getConnectionCredentials().getCredential(), date);
List<Header> requestHeader = new ArrayList<>();
requestHeader.add(new BasicHeader("Date", date));
requestHeader.add(new BasicHeader("Authorization", "AWS " + getConnectionCredentials().getIdentity() + ":" + signature));
String url = "https://" + (bucket == null ? "" : bucket + ".") + endpoint + path;
String rawResponse = HTTPUtils.openUrl(url, "GET", null, null, null, null, requestHeader, null);
JSON json = new XMLSerializer().read(rawResponse);
return (json instanceof JSONObject ? (JSONObject) json : null);
}
private List<JSONObject> getJSONChildren(String key, JSONObject json) {
List<JSONObject> array = new ArrayList<>();
Object obj = json.get(key);
if (obj instanceof JSONObject) {
array.add((JSONObject) obj);
} else if (obj instanceof JSONArray) {
array.addAll((JSONArray) obj);
}
return array;
}
private String signUrl(String rawUrl, Map<String, String> params) {
if (params == null) {
params = new HashMap<>();
}
// http://docs.amazonwebservices.com/general/latest/gr/signature-version-2.html
params.put("AWSAccessKeyId", getConnectionCredentials().getIdentity());
params.put("Timestamp", getFormattedTimestamp(new Date()));
params.put("SignatureVersion", "2");
params.put("SignatureMethod", "HmacSHA256");
params.put("Signature", calculateSignature(rawUrl, getConnectionCredentials().getCredential(), params));
return rawUrl + "?" + getQueryString(params);
}
private String getFormattedTimestamp(Date date) {
SimpleDateFormat df = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
return df.format(date);
}
private String getS3FormattedTimestamp(Date date) {
SimpleDateFormat df = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
df.setTimeZone(TimeZone.getTimeZone("UTC"));
return df.format(date);
}
private String getQueryString(Map<String,String> params) {
StringBuilder query = new StringBuilder();
for (Iterator<Map.Entry<String, String>> iter = params.entrySet().iterator(); iter.hasNext();) {
Map.Entry<String, String> param = iter.next();
query.append(urlEncode(param.getKey())).append("=").append(urlEncode(param.getValue()));
if (iter.hasNext()) query.append("&");
}
return query.toString();
}
private String calculateSignature(String rawUrl, String key, Map<String,String> params) {
StringBuilder canonical = new StringBuilder();
try {
canonical.append("GET").append("\n");
canonical.append(new URL(rawUrl).getHost()).append("\n");
canonical.append("/").append("\n");
SortedMap<String, String> sorted = new TreeMap<>(params);
canonical.append(getQueryString(sorted));
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"));
byte[] sign = mac.doFinal(canonical.toString().getBytes("UTF-8"));
return new String(Base64.encodeBase64(sign));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private String calculateS3Signature(String rawUrl, String key, String date) {
StringBuilder canonical = new StringBuilder();
try {
canonical.append("GET").append("\n");
canonical.append("\n");
canonical.append("\n");
canonical.append(date).append("\n");
canonical.append(rawUrl);
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1"));
byte[] sign = mac.doFinal(canonical.toString().getBytes("UTF-8"));
return new String(Base64.encodeBase64(sign));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private String urlEncode(String value) {
if (value == null) return "";
try {
return URLEncoder.encode(value, "UTF-8")
.replace("+", "%20").replace("*", "%2A")
.replace("%7E", "~");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Returns the human readable name for the region id passed in.
*
* @param regionId the region id to get its human readable name
*
* @return the human readable name or the region id if a human readable name isn't known
*/
public static String getRegionName(String regionId) {
// Map the region ids to human readable names (Known regions as of 16/08/2011)
// Note: This will be unnecessary once jclouds has LocationMetadata
// Note: This will need to be refactored when we get multiple cloud providers supported if jclouds
// isn't updated by then.
if (regionId.equals("ap-northeast-1")) {
return "Asia Pacific (Tokyo)";
} else if (regionId.equals("ap-southeast-1")) {
return "Asia Pacific (Singapore)";
} else if (regionId.equals("eu-west-1")) {
return "EU West (Ireland)";
} else if (regionId.equals("sa-east-1")) {
return "South America (Sao Paulo)";
} else if (regionId.equals("us-east-1")) {
return "US East (Virginia)";
} else if (regionId.equals("us-west-1")) {
return "US West (California)";
} else if (regionId.equals("us-west-2")) {
return "US West (Oregon)";
} else {
return regionId; // Reasonable default
}
}
/**
* Returns the iso3166 code for the region id passed in.
*
* @param regionId the region id to get its iso3166 code
*
* @return the iso3166 code or the region id if a iso3166 code isn't known
*/
public static String getRegionIso3166Code(String regionId) {
if (regionId.equals("ap-northeast-1")) {
return "JP-13";
} else if (regionId.equals("ap-southeast-1")) {
return "SG";
} else if (regionId.equals("eu-west-1")) {
return "IE";
} else if (regionId.equals("sa-east-1")) {
return "BR-SP";
} else if (regionId.equals("us-east-1")) {
return "US-VA";
} else if (regionId.equals("us-west-1")) {
return "US-CA";
} else if (regionId.equals("us-west-2")) {
return "US-OR";
} else {
return regionId; // Reasonable default
}
}
/**
* Returns the human readable name for the hardware id passed in.
*
* @param hardwareId the hardware id to get its human readable name
*
* @return the human readable name or the hardware id if a human readable name isn't known
*/
public static String getHardwareName(String hardwareId) {
// Map the hardware ids to human readable names (Known hardware ids as of 16/08/2011)
// http://aws.amazon.com/ec2/instance-types/
// Note: This will be unnecessary once jclouds has LocationMetadata
// Note: This will need to be refactored when we get multiple cloud providers supported if jclouds
// isn't updated by then.
if (hardwareId.equals("c1.medium")) {
return "High-CPU Medium";
} else if (hardwareId.equals("c1.xlarge")) {
return "High-CPU Extra Large";
} else if (hardwareId.equals("cg1.4xlarge")) {
return "Cluster GPU Quadruple Extra Large";
} else if (hardwareId.equals("cc1.4xlarge")) {
return "Cluster Compute Quadruple Extra Large";
} else if (hardwareId.equals("m1.large")) {
return "Large";
} else if (hardwareId.equals("m1.small")) {
return "Small";
} else if (hardwareId.equals("m1.xlarge")) {
return "Extra Large";
} else if (hardwareId.equals("m2.xlarge")) {
return "High-Memory Extra Large";
} else if (hardwareId.equals("m2.2xlarge")) {
return "High-Memory Double Extra Large";
} else if (hardwareId.equals("m2.4xlarge")) {
return "High-Memory Quadruple Extra Large";
} else if (hardwareId.equals("t1.micro")) {
return "Micro";
} else {
return hardwareId; // Reasonable default
}
}
/**
* {@inheritDoc}
*/
@Override
public void validateConnection() throws InvalidCredentialsException, IOException {
getComputeServiceContext();
getBlobStoreContext();
}
/**
* {@inheritDoc}
*/
@Override
public void cleanUp() {
try {
if (computeServiceContext != null) {
computeServiceContext.close();
}
} catch (Exception e) {
LOGGER.error("Unable to clean up the AWS EC2 connection.", e);
}
try {
if (cloudWatchContext != null) {
cloudWatchContext.close();
}
} catch (Exception e) {
LOGGER.error("Unable to clean up the AWS Cloudwatch connection.", e);
}
try {
if (blobStoreContext != null) {
blobStoreContext.close();
}
} catch (Exception e) {
LOGGER.error("Unable to clean up the AWS Blobstore connection.", e);
}
}
/**
* Returns the jclouds {@link ComputeServiceContext} for AWS EC2.
*
* @return the jclouds compute service context
*
* @throws InvalidCredentialsException if the credentials associated with this client are invalid
*/
public ComputeServiceContext getComputeServiceContext() throws InvalidCredentialsException {
if (computeServiceContext == null) {
Properties overrides = new Properties();
// Empty the default AMI queries for quicker image lookup
overrides.setProperty(AWSEC2Constants.PROPERTY_EC2_AMI_QUERY, "");
overrides.setProperty(AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY, "");
String username = getConnectionCredentials().getIdentity();
String password = getConnectionCredentials().getCredential();
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
throw new InvalidCredentialsException("You must supply an identity/username for cloud connections.");
}
try {
computeServiceContext = ContextBuilder.newBuilder("aws-ec2")
.credentials(username, password)
.modules(ImmutableSet.<Module>of(
new SshjSshClientModule(),
new SLF4JLoggingModule()))
.overrides(overrides)
.buildView(ComputeServiceContext.class);
// since the compute service context doesn't try to auth against AWS until it
// needs to, make a call to listNodes() to force the auth.
computeServiceContext.getComputeService().listNodes();
} catch (AuthorizationException ae) {
throw new InvalidCredentialsException(ae);
}
}
return computeServiceContext;
}
/**
* Returns the jclouds {@link BlobStoreContext} for AWS S3.
*
* @return the jclouds blob store context
*
* @throws InvalidCredentialsException if the credentials associated with this client are invalid
*/
public BlobStoreContext getBlobStoreContext() throws InvalidCredentialsException {
if (blobStoreContext == null) {
String username = getConnectionCredentials().getIdentity();
String password = getConnectionCredentials().getCredential();
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
throw new InvalidCredentialsException("You must supply an identity/username for cloud connections.");
}
try {
blobStoreContext = ContextBuilder.newBuilder("aws-s3")
.credentials(username, password)
.modules(ImmutableSet.<Module>of(new SLF4JLoggingModule()))
.buildView(BlobStoreContext.class);
} catch (AuthorizationException ae) {
throw new InvalidCredentialsException(ae);
}
}
return blobStoreContext;
}
/**
* Returns the jclouds {@link RestContext} for AWS CloudWatch.
*
* @return the jclouds RestContext for AWS Cloudwatch
*
* @throws InvalidCredentialsException if the credentials associated with this client are invalid
*/
public RestContext<CloudWatchApi, CloudWatchAsyncApi> getCloudWatchServiceContext()
throws InvalidCredentialsException {
if (cloudWatchContext == null) {
String username = getConnectionCredentials().getIdentity();
String password = getConnectionCredentials().getCredential();
try {
cloudWatchContext = ContextBuilder.newBuilder("aws-cloudwatch")
.credentials(username, password)
.modules(ImmutableSet.<Module>of(new SLF4JLoggingModule()))
.build();
} catch (AuthorizationException ae) {
throw new InvalidCredentialsException(ae);
}
}
return cloudWatchContext;
}
/**
*
* @param outboundConfiguration
* @return the name of the created bucket
*/
public String createBucket(OutboundConfiguration outboundConfiguration) throws InvalidCredentialsException {
BlobStore s3BlobStore = getBlobStoreContext().getBlobStore();
String bucketName = convertOutboundConnectionToBucketName(outboundConfiguration);
Location location = null;
if (StringUtils.hasText(outboundConfiguration.getDestination())) {
LocationBuilder builder = new LocationBuilder();
builder.id(outboundConfiguration.getDestination());
builder.scope(LocationScope.REGION);
builder.description(outboundConfiguration.getDestination());
location = builder.build();
}
s3BlobStore.createContainerInLocation(location ,bucketName); //defaults to us-standard if location is null
return bucketName;
}
/**
*
* @param outboundConfiguration
* @param payloadIdentifier
* @param payload bytes to be written to S3.
* @return an eTag of the object that was just created.
*/
public String pushToS3(OutboundConfiguration outboundConfiguration, String payloadIdentifier, byte[] payload)
throws InvalidCredentialsException {
BlobStore s3BlobStore = getBlobStoreContext().getBlobStore();
String bucketName = createBucket(outboundConfiguration);
Blob blob = s3BlobStore.blobBuilder(payloadIdentifier).payload(payload).build();
return s3BlobStore.putBlob(bucketName,blob);
}
/**
* Extracts a bucket name from an OutboundConfiguration. If the OutboundConfiguration's namespace property is non-null
* and contains text, that property is used as the bucketName. Otherwise, a default bucketname in the form of
* "com.streamreduce.${account.id}" is used to create the bucket.
* @param outboundConfiguration - The OutboundConfiguration reference representing an outbound operation to S3
* @return Either OutboundConfiguration.getNamespace() or "com.streamreduce.${account.id}" if namespace
* is empty.
*/
String convertOutboundConnectionToBucketName(OutboundConfiguration outboundConfiguration) {
if (StringUtils.hasText(outboundConfiguration.getNamespace())) {
return outboundConfiguration.getNamespace().toLowerCase();
} else {
return BUCKET_PREFIX + "." +
outboundConfiguration.getOriginatingConnection().getAccount().getId().toString().toLowerCase();
}
}
}