diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/MonitoredS3ObjectConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/MonitoredS3ObjectConfiguration.java index ff7d9c601a..adaa1a0046 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/configuration/MonitoredS3ObjectConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/configuration/MonitoredS3ObjectConfiguration.java @@ -7,16 +7,19 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import jakarta.validation.constraints.NotBlank; +import java.net.URI; import java.time.Duration; import java.util.concurrent.ScheduledExecutorService; import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import javax.annotation.Nullable; @JsonTypeName("default") public record MonitoredS3ObjectConfiguration( @NotBlank String s3Region, @NotBlank String s3Bucket, @NotBlank String objectKey, + @Nullable URI endpointOverride, Long maxSize, Duration refreshInterval) implements S3ObjectMonitorFactory { @@ -36,6 +39,10 @@ public record MonitoredS3ObjectConfiguration( public S3ObjectMonitor build(final AwsCredentialsProvider awsCredentialsProvider, final ScheduledExecutorService refreshExecutorService) { + if (endpointOverride != null && !endpointOverride.toString().isEmpty()) { + return S3ObjectMonitor.createCustomS3(awsCredentialsProvider, endpointOverride, s3Region, s3Bucket, objectKey, + maxSize, refreshExecutorService, refreshInterval); + } return new S3ObjectMonitor(awsCredentialsProvider, s3Region, s3Bucket, objectKey, maxSize, refreshExecutorService, refreshInterval); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/s3/S3ObjectMonitor.java b/service/src/main/java/org/whispersystems/textsecuregcm/s3/S3ObjectMonitor.java index d997ad99ab..2a1c0ae6a9 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/s3/S3ObjectMonitor.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/s3/S3ObjectMonitor.java @@ -8,6 +8,7 @@ import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.time.Duration; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -21,10 +22,13 @@ import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.model.HeadObjectRequest; import software.amazon.awssdk.services.s3.model.HeadObjectResponse; +import javax.annotation.Nullable; /** * An S3 object monitor watches a specific object in an S3 bucket and notifies a listener if that object changes. @@ -65,6 +69,28 @@ public S3ObjectMonitor( refreshInterval); } + // allows specifying a custom S3 endpoint + static public S3ObjectMonitor createCustomS3(final AwsCredentialsProvider awsCredentialsProvider, + final URI s3Endpoint, + final String s3Region, + final String s3Bucket, + final String objectKey, + final long maxObjectSize, + final ScheduledExecutorService refreshExecutorService, + final Duration refreshInterval) { + final S3ClientBuilder s3ClientBuilder = S3Client.builder() + .region(Region.of(s3Region)) + .credentialsProvider(awsCredentialsProvider) + .endpointOverride(s3Endpoint) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true).build()); + return new S3ObjectMonitor(s3ClientBuilder.build(), s3Bucket, + objectKey, + maxObjectSize, + refreshExecutorService, + refreshInterval); + } + @VisibleForTesting S3ObjectMonitor( final S3Client s3Client,