Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: HPC Server API
variables:
lastSyncUploadPath: ""
presignedDownloadUrl: ""
lastS3DownloadFileId: ""
lastS3DownloadTaskId: ""
auth:
- id: 236c22c3-dd78-4b41-8176-a23ea4721e7b
type: basic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ name: Download to AWS S3
method: POST
url: "{{baseUrl}}{{basePath}}/v2/dataObject/{{path}}/download"
scripts:
- type: afterResponse
code: "pm.collectionVariables.set('lastS3DownloadFileId', pm.variables.get('fileId')); const res = pm.response.json(); if (res.taskId) { pm.collectionVariables.set('lastS3DownloadTaskId', res.taskId); }"
language: text/javascript
- type: beforeRequest
code: "pm.variables.set('path', pm.collectionVariables.get('lastSyncUploadPath')); "
code: "pm.variables.set('path', pm.collectionVariables.get('lastSyncUploadPath')); const now = new Date(); const ts = (now.getMonth()+1).toString().padStart(2,'0') + '-' + now.getDate().toString().padStart(2,'0') + '-' + now.getFullYear() + '-' + now.getHours().toString().padStart(2,'0') + '-' + now.getMinutes().toString().padStart(2,'0') + '-' + now.getSeconds().toString().padStart(2,'0'); pm.variables.set('fileId', pm.globals.get('aws-folder') + '/file-download-' + ts);"
language: text/javascript
order: 3863696254383541
headers:
Expand All @@ -18,7 +21,7 @@ body:
"s3DownloadDestination": {
"destinationLocation": {
"fileContainerId": "{{aws-bucket}}",
"fileId": "{{aws-folder}}/file-download"
"fileId": "{{fileId}}"
},
"account": {
"accessKey": "{{vault:aws-key}}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$kind: http-request
name: Get S3 Download Task Status
method: GET
url: '{{baseUrl}}{{basePath}}/dataObject/download/{{lastS3DownloadTaskId}}'
order: 3863696254383542
headers:
- key: Accept
value: 'application/json'
4 changes: 4 additions & 0 deletions src/hpc-server/hpc-integration-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@
<groupId>software.amazon.awssdk.crt</groupId>
<artifactId>aws-crt</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gov.nih.nci.hpc.integration.s3.impl;
package gov.nih.nci.hpc.integration.s3.v1.impl;

import static gov.nih.nci.hpc.integration.HpcDataTransferProxy.getArchiveDestinationLocation;
import static gov.nih.nci.hpc.util.HpcUtil.toIntExact;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Distributed under the OSI-approved BSD 3-Clause License. See
* http://ncip.github.com/HPC/LICENSE.txt for details.
*/
package gov.nih.nci.hpc.integration.s3.impl;
package gov.nih.nci.hpc.integration.s3.v1.impl;

import java.util.Base64;
import java.util.HashSet;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* <p>Distributed under the OSI-approved BSD 3-Clause License. See
* http://ncip.github.com/HPC/LICENSE.txt for details.
*/
package gov.nih.nci.hpc.integration.s3.impl;
package gov.nih.nci.hpc.integration.s3.v1.impl;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,30 @@
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.crt.CrtRuntimeException;
import software.amazon.awssdk.crt.Log;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.S3CrtAsyncClientBuilder;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.transfer.s3.S3TransferManager;

/**
* HPC S3 Connection.
* HPC S3 Connection base class. Holds all logic shared between the concrete S3
* connection implementations. The only behavior that differs between
* implementations is how the {@link S3AsyncClient} is built (e.g. AWS CRT vs
* Netty-NIO), which is delegated to the abstract build methods.
*
* @author <a href="mailto:eran.rosenberg@nih.gov">Eran Rosenberg</a>
*/
public class HpcS3Connection {
public abstract class HpcS3Connection {
// ---------------------------------------------------------------------//
// Constants
// ---------------------------------------------------------------------//

// 5GB in bytes
private static final long FIVE_GB = 5368709120L;
protected static final long FIVE_GB = 5368709120L;

// Google Storage S3 URL.
private static final String GOOGLE_STORAGE_URL = "https://storage.googleapis.com";
protected static final String GOOGLE_STORAGE_URL = "https://storage.googleapis.com";

// ---------------------------------------------------------------------//
// Instance members
Expand All @@ -66,25 +66,33 @@ public class HpcS3Connection {

// The multipart upload minimum part size.
@Value("${hpc.integration.s3.minimumUploadPartSize}")
private Long minimumUploadPartSize = null;
protected Long minimumUploadPartSize = null;

// The multipart upload threshold.
@Value("${hpc.integration.s3.multipartUploadThreshold}")
private Long multipartUploadThreshold = null;
protected Long multipartUploadThreshold = null;

// The CRT log file (Optional).
@Value("${hpc.integration.s3.crtLogFile:#{null}}")
private String crtLogFile = null;
// The connection timeout - the time to wait to establish a new connection, in
// milliseconds. Same behavior across all S3 client implementations, so the
// property is shared.
@Value("${hpc.integration.s3.connectionTimeout}")
protected Integer connectionTimeout = null;

// AWS CRT to trust all certs config.
@Value("${hpc.integration.s3.trustAllCerts:false}")
private Boolean trustAllCerts = null;
// The max number of error retries. Same behavior across all S3 client
// implementations, so the property is shared.
@Value("${hpc.integration.s3.maxErrorRetries}")
protected Integer maxErrorRetries = null;

// Disable SSL certificate checking (for development/testing only). Shares the
// property with the v1 client since the behavior is the same.
@Value("${hpc.integration.s3.disableCertChecking:false}")
protected Boolean disableCertChecking = null;

// The executor service to be used by AWSTransferManager
private ExecutorService executorService = null;

// The logger instance.
private final Logger logger = LoggerFactory.getLogger(getClass().getName());
protected final Logger logger = LoggerFactory.getLogger(getClass().getName());

// ---------------------------------------------------------------------//
// Constructors
Expand All @@ -99,7 +107,7 @@ public class HpcS3Connection {
* @param awsTransferManagerThreadPoolSize The thread pool size to be used for
* AWS transfer manager
*/
private HpcS3Connection(String pathStyleAccessEnabledProviders, int awsTransferManagerThreadPoolSize) {
protected HpcS3Connection(String pathStyleAccessEnabledProviders, int awsTransferManagerThreadPoolSize) {
for (String s3Provider : pathStyleAccessEnabledProviders.split(",")) {
this.pathStyleAccessEnabledProviders.add(HpcIntegratedSystem.fromValue(s3Provider));
}
Expand All @@ -110,7 +118,33 @@ private HpcS3Connection(String pathStyleAccessEnabledProviders, int awsTransferM
}

// ---------------------------------------------------------------------//
// Methods
// Abstract Methods
// ---------------------------------------------------------------------//

/**
* Build a S3 async client for a 'S3 3rd Party Provider' account.
*
* @param credentialsProvider The S3 credentials provider.
* @param endpoint The S3 provider endpoint.
* @param pathStyleAccessEnabled true if the S3 3rd Party provider supports path
* style access.
* @param thresholdInBytes The multipart upload threshold in bytes.
* @return A S3 async client.
*/
protected abstract S3AsyncClient buildS3ProviderAsyncClient(StaticCredentialsProvider credentialsProvider,
URI endpoint, boolean pathStyleAccessEnabled, long thresholdInBytes);

/**
* Build a S3 async client for an AWS S3 account.
*
* @param credentialsProvider The AWS credentials provider.
* @param region The AWS account region.
* @return A S3 async client.
*/
protected abstract S3AsyncClient buildAwsAsyncClient(StaticCredentialsProvider credentialsProvider, String region);

// ---------------------------------------------------------------------//
// Public Methods
// ---------------------------------------------------------------------//

/**
Expand Down Expand Up @@ -224,7 +258,7 @@ public HpcIntegratedSystem getS3Provider(Object authenticatedToken) throws HpcEx
// Helper Methods
// ---------------------------------------------------------------------//

private class HpcS3 {
protected class HpcS3 {
private S3TransferManager transferManager = null;
private S3AsyncClient client = null;
private S3Presigner presigner = null;
Expand Down Expand Up @@ -262,24 +296,12 @@ private Object authenticateS3Provider(String username, String password, String u
HpcS3 s3 = new HpcS3();
s3.provider = s3Provider;

try {
// If configured, start the AWS CRT logger.
if (!StringUtils.isEmpty(crtLogFile)) {
Log.initLoggingToFile(Log.LogLevel.Trace, crtLogFile);
}
long thresholdInBytes = url.equalsIgnoreCase(GOOGLE_STORAGE_URL) ? FIVE_GB : multipartUploadThreshold;

// Instantiate a S3 async client.
S3CrtAsyncClientBuilder crtAsyncClientBuilder = S3AsyncClient.crtBuilder()
.credentialsProvider(s3ProviderCredentialsProvider).forcePathStyle(pathStyleAccessEnabled)
.endpointOverride(uri).minimumPartSizeInBytes(minimumUploadPartSize)
.thresholdInBytes(url.equalsIgnoreCase(GOOGLE_STORAGE_URL) ? FIVE_GB : multipartUploadThreshold).maxConcurrency(1);

if (trustAllCerts) {
crtAsyncClientBuilder.httpConfiguration(builder -> builder.trustAllCertificatesEnabled(true));
logger.warn("hpc.integration.s3.trustAllCerts property is set to true. CRT cert validation is off");
}

s3.client = crtAsyncClientBuilder.build();
try {
// Instantiate a S3 async client (implementation specific).
s3.client = buildS3ProviderAsyncClient(s3ProviderCredentialsProvider, uri, pathStyleAccessEnabled,
thresholdInBytes);

// Instantiate the S3 transfer manager.
s3.transferManager = S3TransferManager.builder().s3Client(s3.client).executor(executorService).build();
Expand All @@ -292,7 +314,7 @@ private Object authenticateS3Provider(String username, String password, String u

return s3;

} catch (SdkException | CrtRuntimeException e) {
} catch (SdkException e) {
throw new HpcException(
"[S3] Failed to authenticate S3 Provider: " + s3Provider.value() + "] - " + e.getMessage(),
HpcErrorType.DATA_TRANSFER_ERROR, e);
Expand All @@ -317,15 +339,8 @@ private Object authenticateAWS(String accessKey, String secretKey, String region
s3.provider = HpcIntegratedSystem.AWS;

try {
// If configured, start the AWS CRT logger.
if (!StringUtils.isEmpty(crtLogFile)) {
Log.initLoggingToFile(Log.LogLevel.Trace, crtLogFile);
}

// Instantiate a S3 async client.
s3.client = S3AsyncClient.crtBuilder().credentialsProvider(awsCredentialsProvider).region(Region.of(region))
.minimumPartSizeInBytes(minimumUploadPartSize)
.thresholdInBytes(multipartUploadThreshold).maxConcurrency(1).build();
// Instantiate a S3 async client (implementation specific).
s3.client = buildAwsAsyncClient(awsCredentialsProvider, region);

// Instantiate the S3 transfer manager.
s3.transferManager = S3TransferManager.builder().s3Client(s3.client).executor(executorService).build();
Expand All @@ -336,7 +351,7 @@ private Object authenticateAWS(String accessKey, String secretKey, String region

return s3;

} catch (SdkException | CrtRuntimeException e) {
} catch (SdkException e) {
throw new HpcException("[S3] Failed to authenticate S3 in region " + region + "] - " + e.getMessage(),
HpcErrorType.DATA_TRANSFER_ERROR, e);
}
Expand Down
Loading