org.jboss.as.host.controller.discovery.S3Discovery Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.host.controller.discovery;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MASTER;
import static org.jboss.as.host.controller.HostControllerLogger.ROOT_LOGGER;
import static org.jboss.as.host.controller.HostControllerMessages.MESSAGES;
import static org.jboss.as.host.controller.discovery.Constants.ACCESS_KEY;
import static org.jboss.as.host.controller.discovery.Constants.LOCATION;
import static org.jboss.as.host.controller.discovery.Constants.PREFIX;
import static org.jboss.as.host.controller.discovery.Constants.PRE_SIGNED_DELETE_URL;
import static org.jboss.as.host.controller.discovery.Constants.PRE_SIGNED_PUT_URL;
import static org.jboss.as.host.controller.discovery.Constants.SECRET_ACCESS_KEY;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.host.controller.discovery.S3Util.AWSAuthConnection;
import org.jboss.as.host.controller.discovery.S3Util.Bucket;
import org.jboss.as.host.controller.discovery.S3Util.GetResponse;
import org.jboss.as.host.controller.discovery.S3Util.ListAllMyBucketsResponse;
import org.jboss.as.host.controller.discovery.S3Util.PreSignedUrlParser;
import org.jboss.as.host.controller.discovery.S3Util.S3Object;
import org.jboss.dmr.ModelNode;
/**
* Handle domain controller discovery via Amazon's S3 storage.
* The S3 access code reuses the example shipped by Amazon.
*
* @author Farah Juma
*/
public class S3Discovery implements DiscoveryOption {
// The name of the S3 file that will store the domain controller's host and port
private static final String DC_FILE_NAME="jboss-domain-master-data";
// The access key to AWS (S3)
private String access_key = null;
// The secret access key to AWS (S3)
private String secret_access_key = null;
// The name of the S3 bucket
private String location = null;
// The name of the S3 bucket prefix
private String prefix = null;
// The pre-signed URL for PUTs
private String pre_signed_put_url = null;
// The pre-signed URL for DELETEs
private String pre_signed_delete_url = null;
private AWSAuthConnection conn = null;
private String remoteDcHost;
private int remoteDcPort;
/**
* Create the S3Discovery option.
*
* @param properties map of properties needed to access the S3 bucket
*/
public S3Discovery(Map properties) {
ModelNode accessKeyNode = properties.get(ACCESS_KEY);
access_key = (accessKeyNode == null || !accessKeyNode.isDefined()) ? null : accessKeyNode.asString();
ModelNode secretAccessKeyNode = properties.get(SECRET_ACCESS_KEY);
secret_access_key = (secretAccessKeyNode == null || !secretAccessKeyNode.isDefined()) ? null : secretAccessKeyNode.asString();
ModelNode locationNode = properties.get(LOCATION);
location = (locationNode == null || !locationNode.isDefined()) ? null : locationNode.asString();
ModelNode prefixNode = properties.get(PREFIX);
prefix = (prefixNode == null || !prefixNode.isDefined()) ? null : prefixNode.asString();
ModelNode preSignedPutUrlNode = properties.get(PRE_SIGNED_PUT_URL);
pre_signed_put_url = (preSignedPutUrlNode == null || !preSignedPutUrlNode.isDefined()) ? null : preSignedPutUrlNode.asString();
ModelNode preSignedDeleteUrlNode = properties.get(PRE_SIGNED_DELETE_URL);
pre_signed_delete_url = (preSignedDeleteUrlNode == null || !preSignedDeleteUrlNode.isDefined()) ? null : preSignedDeleteUrlNode.asString();
}
@Override
public void allowDiscovery(String host, int port) {
try {
// Write the domain controller data to an S3 file
writeToFile(new DomainControllerData(host, port), MASTER);
} catch (Exception e) {
ROOT_LOGGER.cannotWriteDomainControllerData(e);
}
}
@Override
public void discover() {
// Read the domain controller data from an S3 file
DomainControllerData data = readFromFile(MASTER);
if (data != null) {
// Validate and set the host and port
String host = data.getHost();
int port = data.getPort();
try {
// Use the static discovery AD's. They don't allow undefined.
StaticDiscoveryResourceDefinition.HOST.getValidator()
.validateParameter(StaticDiscoveryResourceDefinition.HOST.getName(),
host == null? new ModelNode() : new ModelNode(host));
StaticDiscoveryResourceDefinition.PORT.getValidator()
.validateParameter(StaticDiscoveryResourceDefinition.PORT.getName(), new ModelNode(port));
} catch (OperationFailedException e) {
throw new IllegalStateException(e.getFailureDescription().asString());
}
setRemoteDomainControllerHost(host);
setRemoteDomainControllerPort(port);
} else {
throw MESSAGES.failedMarshallingDomainControllerData();
}
}
@Override
public void cleanUp() {
// Remove the S3 file
remove(MASTER);
}
@Override
public String getRemoteDomainControllerHost() {
return remoteDcHost;
}
@Override
public int getRemoteDomainControllerPort() {
return remoteDcPort;
}
@Override
public String toString() {
// TODO consider including 'location' but that may be sensitive data not wanted in logs
return getClass().getSimpleName();
}
/**
* Determine whether or not pre-signed URLs will be used.
*/
private boolean usingPreSignedUrls() {
return pre_signed_put_url != null;
}
/**
* Make sure {@code pre_signed_put_url} and {@code pre_signed_delete_url} are valid.
*/
private void validatePreSignedUrls() {
if (pre_signed_put_url != null && pre_signed_delete_url != null) {
PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url);
PreSignedUrlParser parsedDelete = new PreSignedUrlParser(pre_signed_delete_url);
if (!parsedPut.getBucket().equals(parsedDelete.getBucket()) ||
!parsedPut.getPrefix().equals(parsedDelete.getPrefix())) {
throw MESSAGES.preSignedUrlsMustHaveSamePath();
}
} else if (pre_signed_put_url != null || pre_signed_delete_url != null) {
throw MESSAGES.preSignedUrlsMustBeSetOrUnset();
}
}
/**
* Do the set-up that's needed to access Amazon S3.
*/
private void init() {
validatePreSignedUrls();
try {
conn = new AWSAuthConnection(access_key, secret_access_key);
// Determine the bucket name if prefix is set or if pre-signed URLs are being used
if (prefix != null && prefix.length() > 0) {
ListAllMyBucketsResponse bucket_list = conn.listAllMyBuckets(null);
List buckets = bucket_list.entries;
if (buckets != null) {
boolean found = false;
for (Object tmp : buckets) {
if (tmp instanceof Bucket) {
Bucket bucket = (Bucket) tmp;
if (bucket.name.startsWith(prefix)) {
location = bucket.name;
found = true;
}
}
}
if (!found) {
location = prefix + "-" + java.util.UUID.randomUUID().toString();
}
}
}
if (usingPreSignedUrls()) {
PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url);
location = parsedPut.getBucket();
}
if (!conn.checkBucketExists(location)) {
conn.createBucket(location, AWSAuthConnection.LOCATION_DEFAULT, null).connection.getResponseMessage();
}
} catch (Exception e) {
throw MESSAGES.cannotAccessS3Bucket(location, e.getLocalizedMessage());
}
}
/**
* Read the domain controller data from an S3 file.
*
* @param directoryName the name of the directory in the bucket that contains the S3 file
* @return the domain controller data
*/
private DomainControllerData readFromFile(String directoryName) {
if(directoryName == null) {
return null;
}
if (conn == null) {
init();
}
DomainControllerData data = null;
try {
if (usingPreSignedUrls()) {
PreSignedUrlParser parsedPut = new PreSignedUrlParser(pre_signed_put_url);
directoryName = parsedPut.getPrefix();
}
String key = S3Util.sanitize(directoryName) + "/" + S3Util.sanitize(DC_FILE_NAME);
GetResponse val = conn.get(location, key, null);
if (val.object != null) {
byte[] buf = val.object.data;
if (buf != null && buf.length > 0) {
try {
data = S3Util.domainControllerDataFromByteBuffer(buf);
} catch (Exception e) {
throw MESSAGES.failedMarshallingDomainControllerData();
}
}
}
return data;
} catch (IOException e) {
throw MESSAGES.cannotAccessS3File(e.getLocalizedMessage());
}
}
/**
* Write the domain controller data to an S3 file.
*
* @param data the domain controller data
* @param domainName the name of the directory in the bucket to write the S3 file to
* @throws IOException
*/
private void writeToFile(DomainControllerData data, String domainName) throws IOException {
if(domainName == null || data == null) {
return;
}
if (conn == null) {
init();
}
try {
String key = S3Util.sanitize(domainName) + "/" + S3Util.sanitize(DC_FILE_NAME);
byte[] buf = S3Util.domainControllerDataToByteBuffer(data);
S3Object val = new S3Object(buf, null);
if (usingPreSignedUrls()) {
Map headers = new TreeMap();
headers.put("x-amz-acl", Arrays.asList("public-read"));
conn.put(pre_signed_put_url, val, headers).connection.getResponseMessage();
} else {
Map headers = new TreeMap();
headers.put("Content-Type", Arrays.asList("text/plain"));
conn.put(location, key, val, headers).connection.getResponseMessage();
}
}
catch(Exception e) {
throw MESSAGES.cannotWriteToS3File(e.getLocalizedMessage());
}
}
/**
* Remove the S3 file that contains the domain controller data.
*
* @param directoryName the name of the directory that contains the S3 file
*/
private void remove(String directoryName) {
if ((directoryName == null) || (conn == null))
return;
String key = S3Util.sanitize(directoryName) + "/" + S3Util.sanitize(DC_FILE_NAME);
try {
Map headers = new TreeMap();
headers.put("Content-Type", Arrays.asList("text/plain"));
if (usingPreSignedUrls()) {
conn.delete(pre_signed_delete_url).connection.getResponseMessage();
} else {
conn.delete(location, key, headers).connection.getResponseMessage();
}
}
catch(Exception e) {
ROOT_LOGGER.cannotRemoveS3File(e);
}
}
private void setRemoteDomainControllerHost(String host) {
remoteDcHost = host;
}
private void setRemoteDomainControllerPort(int port) {
remoteDcPort = port;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy