mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 00:38:04 +01:00
Add tests for WhisperServerService#run
Additionally, `LocalWhisperServerService` may be used for integration testing.
This commit is contained in:
@@ -15,37 +15,40 @@ import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.Cdn3StorageManagerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ClientCdnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ClientReleaseConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DefaultAwsCredentialsFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DogstatsdConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamicConfigurationManagerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbTables;
|
||||
import org.whispersystems.textsecuregcm.configuration.ExternalRequestFilterConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.FaultTolerantRedisClusterFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.FcmConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.GcpAttachmentsConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.GenericZkConfig;
|
||||
import org.whispersystems.textsecuregcm.configuration.HCaptchaConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.HCaptchaClientFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.LinkDeviceSecretConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MaxDeviceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MessageByteLimitCardinalityEstimatorConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MessageCacheConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.NoiseWebSocketTunnelConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.PaymentsServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RedisClusterConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ProvisioningConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.RegistrationServiceClientFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.RemoteConfigConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.ReportMessageConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery2Configuration;
|
||||
import org.whispersystems.textsecuregcm.configuration.SecureValueRecovery3Configuration;
|
||||
@@ -69,6 +72,11 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@JsonProperty
|
||||
private TlsKeyStoreConfiguration tlsKeyStore;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
AwsCredentialsProviderFactory awsCredentialsProvider = new DefaultAwsCredentialsFactory();
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
@@ -82,7 +90,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DynamoDbClientConfiguration dynamoDbClientConfiguration;
|
||||
private DynamoDbClientFactory dynamoDbClient;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@@ -117,22 +125,22 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private DogstatsdConfiguration dogstatsd = new DogstatsdConfiguration();
|
||||
private DatadogConfiguration dogstatsd = new DogstatsdConfiguration();
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration cacheCluster;
|
||||
private FaultTolerantRedisClusterFactory cacheCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisConfiguration pubsub;
|
||||
private FaultTolerantRedisClusterFactory metricsCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration metricsCluster;
|
||||
private ProvisioningConfiguration provisioning;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@@ -151,12 +159,12 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration pushSchedulerCluster;
|
||||
private FaultTolerantRedisClusterFactory pushSchedulerCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration rateLimitersCluster;
|
||||
private FaultTolerantRedisClusterFactory rateLimitersCluster;
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
@@ -166,7 +174,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@NotNull
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private RedisClusterConfiguration clientPresenceCluster;
|
||||
private FaultTolerantRedisClusterFactory clientPresenceCluster;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@@ -201,7 +209,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private HCaptchaConfiguration hCaptcha;
|
||||
private HCaptchaClientFactory hCaptcha;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@@ -246,7 +254,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private AppConfigConfiguration appConfig;
|
||||
private DynamicConfigurationManagerFactory appConfig;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@@ -270,12 +278,12 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
|
||||
@Valid
|
||||
@JsonProperty
|
||||
private SpamFilterConfiguration spamFilterConfiguration;
|
||||
private SpamFilterConfiguration spamFilter;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private RegistrationServiceConfiguration registrationService;
|
||||
private RegistrationServiceClientFactory registrationService;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@@ -305,28 +313,28 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private VirtualThreadConfiguration virtualThreadConfiguration = new VirtualThreadConfiguration(Duration.ofMillis(1));
|
||||
private VirtualThreadConfiguration virtualThread = new VirtualThreadConfiguration(Duration.ofMillis(1));
|
||||
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private MonitoredS3ObjectConfiguration maxmindCityDatabase;
|
||||
private S3ObjectMonitorFactory maxmindCityDatabase;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private MonitoredS3ObjectConfiguration callingTurnDnsRecords;
|
||||
private S3ObjectMonitorFactory callingTurnDnsRecords;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private MonitoredS3ObjectConfiguration callingTurnPerformanceTable;
|
||||
private S3ObjectMonitorFactory callingTurnPerformanceTable;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private MonitoredS3ObjectConfiguration callingTurnManualTable;
|
||||
private S3ObjectMonitorFactory callingTurnManualTable;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@@ -342,6 +350,10 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return tlsKeyStore;
|
||||
}
|
||||
|
||||
public AwsCredentialsProviderFactory getAwsCredentialsConfiguration() {
|
||||
return awsCredentialsProvider;
|
||||
}
|
||||
|
||||
public StripeConfiguration getStripe() {
|
||||
return stripe;
|
||||
}
|
||||
@@ -350,15 +362,15 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return braintree;
|
||||
}
|
||||
|
||||
public DynamoDbClientConfiguration getDynamoDbClientConfiguration() {
|
||||
return dynamoDbClientConfiguration;
|
||||
public DynamoDbClientFactory getDynamoDbClientConfiguration() {
|
||||
return dynamoDbClient;
|
||||
}
|
||||
|
||||
public DynamoDbTables getDynamoDbTables() {
|
||||
return dynamoDbTables;
|
||||
}
|
||||
|
||||
public HCaptchaConfiguration getHCaptchaConfiguration() {
|
||||
public HCaptchaClientFactory getHCaptchaConfiguration() {
|
||||
return hCaptcha;
|
||||
}
|
||||
|
||||
@@ -378,15 +390,15 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return gcpAttachments;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getCacheClusterConfiguration() {
|
||||
public FaultTolerantRedisClusterFactory getCacheClusterConfiguration() {
|
||||
return cacheCluster;
|
||||
}
|
||||
|
||||
public RedisConfiguration getPubsubCacheConfiguration() {
|
||||
return pubsub;
|
||||
public ProvisioningConfiguration getProvisioningConfiguration() {
|
||||
return provisioning;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getMetricsClusterConfiguration() {
|
||||
public FaultTolerantRedisClusterFactory getMetricsClusterConfiguration() {
|
||||
return metricsCluster;
|
||||
}
|
||||
|
||||
@@ -410,15 +422,15 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return messageCache;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getClientPresenceClusterConfiguration() {
|
||||
public FaultTolerantRedisClusterFactory getClientPresenceClusterConfiguration() {
|
||||
return clientPresenceCluster;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getPushSchedulerCluster() {
|
||||
public FaultTolerantRedisClusterFactory getPushSchedulerCluster() {
|
||||
return pushSchedulerCluster;
|
||||
}
|
||||
|
||||
public RedisClusterConfiguration getRateLimitersCluster() {
|
||||
public FaultTolerantRedisClusterFactory getRateLimitersCluster() {
|
||||
return rateLimitersCluster;
|
||||
}
|
||||
|
||||
@@ -446,7 +458,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return cdn3StorageManager;
|
||||
}
|
||||
|
||||
public DogstatsdConfiguration getDatadogConfiguration() {
|
||||
public DatadogConfiguration getDatadogConfiguration() {
|
||||
return dogstatsd;
|
||||
}
|
||||
|
||||
@@ -489,7 +501,7 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
return remoteConfig;
|
||||
}
|
||||
|
||||
public AppConfigConfiguration getAppConfig() {
|
||||
public DynamicConfigurationManagerFactory getAppConfig() {
|
||||
return appConfig;
|
||||
}
|
||||
|
||||
@@ -510,10 +522,10 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
}
|
||||
|
||||
public SpamFilterConfiguration getSpamFilterConfiguration() {
|
||||
return spamFilterConfiguration;
|
||||
return spamFilter;
|
||||
}
|
||||
|
||||
public RegistrationServiceConfiguration getRegistrationServiceConfiguration() {
|
||||
public RegistrationServiceClientFactory getRegistrationServiceConfiguration() {
|
||||
return registrationService;
|
||||
}
|
||||
|
||||
@@ -538,22 +550,22 @@ public class WhisperServerConfiguration extends Configuration {
|
||||
}
|
||||
|
||||
public VirtualThreadConfiguration getVirtualThreadConfiguration() {
|
||||
return virtualThreadConfiguration;
|
||||
return virtualThread;
|
||||
}
|
||||
|
||||
public MonitoredS3ObjectConfiguration getMaxmindCityDatabase() {
|
||||
public S3ObjectMonitorFactory getMaxmindCityDatabase() {
|
||||
return maxmindCityDatabase;
|
||||
}
|
||||
|
||||
public MonitoredS3ObjectConfiguration getCallingTurnDnsRecords() {
|
||||
public S3ObjectMonitorFactory getCallingTurnDnsRecords() {
|
||||
return callingTurnDnsRecords;
|
||||
}
|
||||
|
||||
public MonitoredS3ObjectConfiguration getCallingTurnPerformanceTable() {
|
||||
public S3ObjectMonitorFactory getCallingTurnPerformanceTable() {
|
||||
return callingTurnPerformanceTable;
|
||||
}
|
||||
|
||||
public MonitoredS3ObjectConfiguration getCallingTurnManualTable() {
|
||||
public S3ObjectMonitorFactory getCallingTurnManualTable() {
|
||||
return callingTurnManualTable;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,7 @@ package org.whispersystems.textsecuregcm;
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.api.gax.batching.BatchingSettings;
|
||||
import com.google.api.gax.batching.FlowControlSettings;
|
||||
import com.google.api.gax.batching.FlowController;
|
||||
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||
import com.google.auth.oauth2.ExternalAccountCredentials;
|
||||
import com.google.cloud.pubsub.v1.Publisher;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.pubsub.v1.TopicName;
|
||||
import io.dropwizard.auth.AuthDynamicFeature;
|
||||
import io.dropwizard.auth.AuthFilter;
|
||||
import io.dropwizard.auth.AuthValueFactoryProvider;
|
||||
@@ -27,7 +20,6 @@ import io.dropwizard.core.server.DefaultServerFactory;
|
||||
import io.dropwizard.core.setup.Bootstrap;
|
||||
import io.dropwizard.core.setup.Environment;
|
||||
import io.dropwizard.jetty.HttpsConnectorFactory;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
|
||||
import io.lettuce.core.metrics.MicrometerOptions;
|
||||
@@ -36,9 +28,7 @@ import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor;
|
||||
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -232,7 +222,6 @@ import org.whispersystems.textsecuregcm.subscriptions.BankMandateTranslator;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.BraintreeManager;
|
||||
import org.whispersystems.textsecuregcm.subscriptions.StripeManager;
|
||||
import org.whispersystems.textsecuregcm.util.BufferingInterceptor;
|
||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
||||
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.UsernameHashZkProofVerifier;
|
||||
@@ -263,9 +252,7 @@ import org.whispersystems.websocket.WebSocketResourceProviderFactory;
|
||||
import org.whispersystems.websocket.setup.WebSocketEnvironment;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
@@ -279,9 +266,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
public static final String SECRETS_BUNDLE_FILE_NAME_PROPERTY = "secrets.bundle.filename";
|
||||
|
||||
public static final software.amazon.awssdk.auth.credentials.AwsCredentialsProvider AWSSDK_CREDENTIALS_PROVIDER =
|
||||
WebIdentityTokenFileCredentialsProvider.create();
|
||||
|
||||
@Override
|
||||
public void initialize(final Bootstrap<WhisperServerConfiguration> bootstrap) {
|
||||
// `SecretStore` needs to be initialized before Dropwizard reads the main application config file.
|
||||
@@ -328,16 +312,15 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
final Clock clock = Clock.systemUTC();
|
||||
final int availableProcessors = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
final AwsCredentialsProvider awsCredentialsProvider = config.getAwsCredentialsConfiguration().build();
|
||||
|
||||
UncaughtExceptionHandler.register();
|
||||
|
||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||
new DynamicConfigurationManager<>(config.getAppConfig().getApplication(),
|
||||
config.getAppConfig().getEnvironment(),
|
||||
config.getAppConfig().getConfigurationName(),
|
||||
DynamicConfiguration.class, dynamicConfigurationExecutor);
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = config.getAppConfig()
|
||||
.build(DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||
dynamicConfigurationManager.start();
|
||||
|
||||
MetricsUtil.configureRegistries(config, environment, dynamicConfigurationManager);
|
||||
@@ -362,11 +345,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
BankMandateTranslator bankMandateTranslator = new BankMandateTranslator(headerControlledResourceBundleLookup);
|
||||
|
||||
environment.lifecycle().manage(new ManagedAwsCrt());
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(config.getDynamoDbClientConfiguration(),
|
||||
AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = config.getDynamoDbClientConfiguration()
|
||||
.buildAsyncClient(awsCredentialsProvider);
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(config.getDynamoDbClientConfiguration(),
|
||||
AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbClient dynamoDbClient = config.getDynamoDbClientConfiguration().buildSyncClient(awsCredentialsProvider);
|
||||
|
||||
BlockingQueue<Runnable> messageDeletionQueue = new LinkedBlockingQueue<>();
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "messageDeletionQueueSize"), Collections.emptyList(),
|
||||
@@ -428,18 +410,19 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
.build();
|
||||
ConnectionEventLogger.logConnectionEvents(sharedClientResources);
|
||||
|
||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache",
|
||||
config.getCacheClusterConfiguration(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
||||
config.getMessageCacheConfiguration().getRedisClusterConfiguration(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
||||
config.getClientPresenceClusterConfiguration(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster metricsCluster = new FaultTolerantRedisCluster("metrics",
|
||||
config.getMetricsClusterConfiguration(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
||||
config.getPushSchedulerCluster(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
||||
config.getRateLimitersCluster(), sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster cacheCluster = config.getCacheClusterConfiguration()
|
||||
.build("main_cache", sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster messagesCluster =
|
||||
config.getMessageCacheConfiguration().getRedisClusterConfiguration()
|
||||
.build("messages", sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster clientPresenceCluster = config.getClientPresenceClusterConfiguration()
|
||||
.build("client_presence", sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster metricsCluster = config.getMetricsClusterConfiguration()
|
||||
.build("metrics", sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster pushSchedulerCluster = config.getPushSchedulerCluster().build("push_scheduler",
|
||||
sharedClientResources.mutate());
|
||||
FaultTolerantRedisCluster rateLimitersCluster = config.getRateLimitersCluster().build("rate_limiters",
|
||||
sharedClientResources.mutate());
|
||||
|
||||
final BlockingQueue<Runnable> keyspaceNotificationDispatchQueue = new ArrayBlockingQueue<>(100_000);
|
||||
Metrics.gaugeCollectionSize(name(getClass(), "keyspaceNotificationDispatchQueueSize"), Collections.emptyList(),
|
||||
@@ -551,14 +534,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
registrationRecoveryPasswords);
|
||||
UsernameHashZkProofVerifier usernameHashZkProofVerifier = new UsernameHashZkProofVerifier();
|
||||
|
||||
RegistrationServiceClient registrationServiceClient = new RegistrationServiceClient(
|
||||
config.getRegistrationServiceConfiguration().host(),
|
||||
config.getRegistrationServiceConfiguration().port(),
|
||||
config.getRegistrationServiceConfiguration().credentialConfigurationJson(),
|
||||
config.getRegistrationServiceConfiguration().identityTokenAudience(),
|
||||
config.getRegistrationServiceConfiguration().registrationCaCertificate(),
|
||||
registrationCallbackExecutor,
|
||||
registrationIdentityTokenRefreshExecutor);
|
||||
RegistrationServiceClient registrationServiceClient = config.getRegistrationServiceConfiguration()
|
||||
.build(environment, registrationCallbackExecutor, registrationIdentityTokenRefreshExecutor);
|
||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(svr2CredentialsGenerator,
|
||||
secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
|
||||
SecureStorageClient secureStorageClient = new SecureStorageClient(storageCredentialsGenerator,
|
||||
@@ -595,9 +572,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
apnPushNotificationScheduler, pushLatencyManager);
|
||||
RateLimiters rateLimiters = RateLimiters.createAndValidate(config.getLimitsConfiguration(),
|
||||
dynamicConfigurationManager, rateLimitersCluster);
|
||||
ProvisioningManager provisioningManager = new ProvisioningManager(config.getPubsubCacheConfiguration().getUri(),
|
||||
sharedClientResources, config.getPubsubCacheConfiguration().getTimeout(),
|
||||
config.getPubsubCacheConfiguration().getCircuitBreakerConfiguration());
|
||||
ProvisioningManager provisioningManager = new ProvisioningManager(
|
||||
config.getProvisioningConfiguration().pubsub().build(sharedClientResources),
|
||||
config.getProvisioningConfiguration().circuitBreaker());
|
||||
IssuedReceiptsManager issuedReceiptsManager = new IssuedReceiptsManager(
|
||||
config.getDynamoDbTables().getIssuedReceipts().getTableName(),
|
||||
config.getDynamoDbTables().getIssuedReceipts().getExpiration(),
|
||||
@@ -635,12 +612,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
"message_byte_limit",
|
||||
config.getMessageByteLimitCardinalityEstimator().period());
|
||||
|
||||
HCaptchaClient hCaptchaClient = new HCaptchaClient(
|
||||
config.getHCaptchaConfiguration().getApiKey().value(),
|
||||
hcaptchaRetryExecutor,
|
||||
config.getHCaptchaConfiguration().getCircuitBreaker(),
|
||||
config.getHCaptchaConfiguration().getRetry(),
|
||||
dynamicConfigurationManager);
|
||||
HCaptchaClient hCaptchaClient = config.getHCaptchaConfiguration()
|
||||
.build(hcaptchaRetryExecutor, dynamicConfigurationManager);
|
||||
HttpClient shortCodeRetrieverHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2)
|
||||
.connectTimeout(Duration.ofSeconds(10)).build();
|
||||
ShortCodeExpander shortCodeRetriever = new ShortCodeExpander(shortCodeRetrieverHttpClient, config.getShortCodeRetrieverConfiguration().baseUrl());
|
||||
@@ -652,8 +625,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
ChangeNumberManager changeNumberManager = new ChangeNumberManager(messageSender, accountsManager);
|
||||
|
||||
HttpClient currencyClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).connectTimeout(Duration.ofSeconds(10)).build();
|
||||
FixerClient fixerClient = new FixerClient(currencyClient, config.getPaymentsServiceConfiguration().fixerApiKey().value());
|
||||
CoinMarketCapClient coinMarketCapClient = new CoinMarketCapClient(currencyClient, config.getPaymentsServiceConfiguration().coinMarketCapApiKey().value(), config.getPaymentsServiceConfiguration().coinMarketCapCurrencyIds());
|
||||
FixerClient fixerClient = config.getPaymentsServiceConfiguration().externalClients()
|
||||
.buildFixerClient(currencyClient);
|
||||
CoinMarketCapClient coinMarketCapClient = config.getPaymentsServiceConfiguration().externalClients()
|
||||
.buildCoinMarketCapClient(currencyClient);
|
||||
CurrencyConversionManager currencyManager = new CurrencyConversionManager(fixerClient, coinMarketCapClient,
|
||||
cacheCluster, config.getPaymentsServiceConfiguration().paymentCurrencies(), recurringJobExecutor, Clock.systemUTC());
|
||||
VirtualThreadPinEventMonitor virtualThreadPinEventMonitor = new VirtualThreadPinEventMonitor(
|
||||
@@ -661,39 +636,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
() -> dynamicConfigurationManager.getConfiguration().getVirtualThreads().allowedPinEvents(),
|
||||
config.getVirtualThreadConfiguration().pinEventThreshold());
|
||||
|
||||
final Publisher pubSubPublisher;
|
||||
{
|
||||
final FlowControlSettings flowControlSettings = FlowControlSettings.newBuilder()
|
||||
.setLimitExceededBehavior(FlowController.LimitExceededBehavior.ThrowException)
|
||||
.setMaxOutstandingElementCount(100L)
|
||||
.setMaxOutstandingRequestBytes(16 * 1024 * 1024L) // 16MB
|
||||
.build();
|
||||
|
||||
final BatchingSettings batchingSettings = BatchingSettings.newBuilder()
|
||||
.setFlowControlSettings(flowControlSettings)
|
||||
.setDelayThreshold(org.threeten.bp.Duration.ofMillis(10))
|
||||
// These thresholds are actually the default, setting them explicitly since creating a custom batchingSettings resets them
|
||||
.setElementCountThreshold(100L)
|
||||
.setRequestByteThreshold(5000L)
|
||||
.build();
|
||||
|
||||
try (final ByteArrayInputStream credentialConfigInputStream =
|
||||
new ByteArrayInputStream(config.getBraintree().pubSubCredentialConfiguration().getBytes(StandardCharsets.UTF_8))) {
|
||||
|
||||
pubSubPublisher = Publisher.newBuilder(TopicName.of(config.getBraintree().pubSubProject(), config.getBraintree().pubSubTopic()))
|
||||
.setCredentialsProvider(FixedCredentialsProvider.create(ExternalAccountCredentials.fromStream(credentialConfigInputStream)))
|
||||
.setBatchingSettings(batchingSettings)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
StripeManager stripeManager = new StripeManager(config.getStripe().apiKey().value(), subscriptionProcessorExecutor,
|
||||
config.getStripe().idempotencyKeyGenerator().value(), config.getStripe().boostDescription(), config.getStripe().supportedCurrenciesByPaymentMethod());
|
||||
BraintreeManager braintreeManager = new BraintreeManager(config.getBraintree().merchantId(),
|
||||
config.getBraintree().publicKey(), config.getBraintree().privateKey().value(),
|
||||
config.getBraintree().environment(),
|
||||
config.getBraintree().supportedCurrenciesByPaymentMethod(), config.getBraintree().merchantAccounts(),
|
||||
config.getBraintree().graphqlUrl(), currencyManager, pubSubPublisher, config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor,
|
||||
config.getBraintree().graphqlUrl(), currencyManager, config.getBraintree().pubSubPublisher().build(),
|
||||
config.getBraintree().circuitBreaker(), subscriptionProcessorExecutor,
|
||||
subscriptionProcessorRetryExecutor);
|
||||
|
||||
environment.lifecycle().manage(apnSender);
|
||||
@@ -708,10 +658,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
final RegistrationCaptchaManager registrationCaptchaManager = new RegistrationCaptchaManager(captchaChecker);
|
||||
|
||||
StaticCredentialsProvider cdnCredentialsProvider = StaticCredentialsProvider
|
||||
.create(AwsBasicCredentials.create(
|
||||
config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value()));
|
||||
AwsCredentialsProvider cdnCredentialsProvider = config.getCdnConfiguration().credentials().build();
|
||||
S3Client cdnS3Client = S3Client.builder()
|
||||
.credentialsProvider(cdnCredentialsProvider)
|
||||
.region(Region.of(config.getCdnConfiguration().region()))
|
||||
@@ -730,8 +677,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
config.getGcpAttachmentsConfiguration().rsaSigningKey().value());
|
||||
|
||||
PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().accessKey().value());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().accessSecret().value(),
|
||||
config.getCdnConfiguration().bucket(), config.getCdnConfiguration().credentials().accessKeyId().value());
|
||||
PolicySigner profileCdnPolicySigner = new PolicySigner(
|
||||
config.getCdnConfiguration().credentials().secretAccessKey().value(),
|
||||
config.getCdnConfiguration().region());
|
||||
|
||||
ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().serverSecret().value());
|
||||
@@ -768,23 +716,27 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
|
||||
MaxMindDatabaseManager geoIpCityDatabaseManager = new MaxMindDatabaseManager(
|
||||
recurringConfigSyncExecutor,
|
||||
awsCredentialsProvider,
|
||||
config.getMaxmindCityDatabase(),
|
||||
"city"
|
||||
);
|
||||
environment.lifecycle().manage(geoIpCityDatabaseManager);
|
||||
CallDnsRecordsManager callDnsRecordsManager = new CallDnsRecordsManager(
|
||||
recurringConfigSyncExecutor,
|
||||
awsCredentialsProvider,
|
||||
config.getCallingTurnDnsRecords()
|
||||
);
|
||||
environment.lifecycle().manage(callDnsRecordsManager);
|
||||
CallRoutingTableManager callRoutingTableManager = new CallRoutingTableManager(
|
||||
recurringConfigSyncExecutor,
|
||||
awsCredentialsProvider,
|
||||
config.getCallingTurnPerformanceTable(),
|
||||
"Performance"
|
||||
);
|
||||
environment.lifecycle().manage(callRoutingTableManager);
|
||||
CallRoutingTableManager manualCallRoutingTableManager = new CallRoutingTableManager(
|
||||
recurringConfigSyncExecutor,
|
||||
awsCredentialsProvider,
|
||||
config.getCallingTurnManualTable(),
|
||||
"Manual"
|
||||
);
|
||||
@@ -978,8 +930,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
|
||||
registrationLockVerificationManager, rateLimiters),
|
||||
new ArtController(rateLimiters, artCredentialsGenerator),
|
||||
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(),
|
||||
config.getAwsAttachmentsConfiguration().accessSecret().value(),
|
||||
new AttachmentControllerV2(rateLimiters,
|
||||
config.getAwsAttachmentsConfiguration().credentials().accessKeyId().value(),
|
||||
config.getAwsAttachmentsConfiguration().credentials().secretAccessKey().value(),
|
||||
config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
|
||||
new AttachmentControllerV3(rateLimiters, gcsAttachmentGenerator),
|
||||
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, tusAttachmentGenerator,
|
||||
@@ -1012,8 +965,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
new SecureStorageController(storageCredentialsGenerator),
|
||||
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
|
||||
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().accessKey().value(),
|
||||
config.getCdnConfiguration().accessSecret().value(), config.getCdnConfiguration().region(),
|
||||
new StickerController(rateLimiters, config.getCdnConfiguration().credentials().accessKeyId().value(),
|
||||
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
||||
config.getCdnConfiguration().bucket()),
|
||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
||||
|
||||
@@ -11,18 +11,18 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
|
||||
public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed {
|
||||
|
||||
@@ -38,20 +38,10 @@ public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed
|
||||
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
|
||||
.build();
|
||||
|
||||
public CallDnsRecordsManager(
|
||||
@Nonnull final ScheduledExecutorService executorService,
|
||||
@Nonnull final MonitoredS3ObjectConfiguration configuration
|
||||
){
|
||||
this.objectMonitor = new S3ObjectMonitor(
|
||||
configuration.s3Region(),
|
||||
configuration.s3Bucket(),
|
||||
configuration.objectKey(),
|
||||
configuration.maxSize(),
|
||||
executorService,
|
||||
configuration.refreshInterval(),
|
||||
this::handleDatabaseChanged
|
||||
);
|
||||
public CallDnsRecordsManager(final ScheduledExecutorService executorService,
|
||||
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration) {
|
||||
|
||||
this.objectMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||
this.callDnsRecords.set(CallDnsRecords.empty());
|
||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallDnsRecordsManager.class, "refresh"));
|
||||
}
|
||||
@@ -74,14 +64,12 @@ public class CallDnsRecordsManager implements Supplier<CallDnsRecords>, Managed
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
Managed.super.start();
|
||||
objectMonitor.start();
|
||||
objectMonitor.start(this::handleDatabaseChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
objectMonitor.stop();
|
||||
Managed.super.stop();
|
||||
callDnsRecords.getAndSet(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,18 @@ package org.whispersystems.textsecuregcm.calls.routing;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
|
||||
public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Managed {
|
||||
|
||||
@@ -33,21 +33,11 @@ public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Mana
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CallRoutingTableManager.class);
|
||||
|
||||
public CallRoutingTableManager(
|
||||
@Nonnull final ScheduledExecutorService executorService,
|
||||
@Nonnull final MonitoredS3ObjectConfiguration configuration,
|
||||
@Nonnull final String tableTag
|
||||
){
|
||||
this.objectMonitor = new S3ObjectMonitor(
|
||||
configuration.s3Region(),
|
||||
configuration.s3Bucket(),
|
||||
configuration.objectKey(),
|
||||
configuration.maxSize(),
|
||||
executorService,
|
||||
configuration.refreshInterval(),
|
||||
this::handleDatabaseChanged
|
||||
);
|
||||
public CallRoutingTableManager(final ScheduledExecutorService executorService,
|
||||
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration,
|
||||
final String tableTag) {
|
||||
|
||||
this.objectMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||
this.tableTag = tableTag;
|
||||
this.routingTable.set(CallRoutingTable.empty());
|
||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(CallRoutingTableManager.class, tableTag));
|
||||
@@ -67,13 +57,11 @@ public class CallRoutingTableManager implements Supplier<CallRoutingTable>, Mana
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
Managed.super.start();
|
||||
objectMonitor.start();
|
||||
objectMonitor.start(this::handleDatabaseChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
Managed.super.stop();
|
||||
objectMonitor.stop();
|
||||
routingTable.getAndSet(null);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
|
||||
public class AppConfigConfiguration {
|
||||
@JsonTypeName("default")
|
||||
public class AppConfigConfiguration implements DynamicConfigurationManagerFactory {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
@@ -29,4 +33,11 @@ public class AppConfigConfiguration {
|
||||
public String getConfigurationName() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DynamicConfigurationManager<T> build(Class<T> klazz, ScheduledExecutorService scheduledExecutorService,
|
||||
AwsCredentialsProvider awsCredentialsProvider) {
|
||||
return new DynamicConfigurationManager<>(application, environment, configuration, awsCredentialsProvider, klazz,
|
||||
scheduledExecutorService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record AwsAttachmentsConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
public record AwsAttachmentsConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultAwsCredentialsFactory.class)
|
||||
public interface AwsCredentialsProviderFactory extends Discoverable {
|
||||
|
||||
AwsCredentialsProvider build();
|
||||
}
|
||||
@@ -32,9 +32,7 @@ public record BraintreeConfiguration(@NotBlank String merchantId,
|
||||
@NotBlank String graphqlUrl,
|
||||
@NotEmpty Map<String, String> merchantAccounts,
|
||||
@NotNull @Valid CircuitBreakerConfiguration circuitBreaker,
|
||||
@NotBlank String pubSubProject,
|
||||
@NotBlank String pubSubTopic,
|
||||
@NotBlank String pubSubCredentialConfiguration) {
|
||||
@Valid @NotNull PubSubPublisherFactory pubSubPublisher) {
|
||||
|
||||
public BraintreeConfiguration {
|
||||
if (circuitBreaker == null) {
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record CdnConfiguration(@NotNull SecretString accessKey,
|
||||
@NotNull SecretString accessSecret,
|
||||
public record CdnConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials,
|
||||
@NotBlank String bucket,
|
||||
@NotBlank String region) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import io.micrometer.statsd.StatsdConfig;
|
||||
import java.time.Duration;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DogstatsdConfiguration.class)
|
||||
public interface DatadogConfiguration extends StatsdConfig, Discoverable {
|
||||
|
||||
String getEnvironment();
|
||||
|
||||
Duration getShutdownWaitDuration();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.credentials.WebIdentityTokenFileCredentialsProvider;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record DefaultAwsCredentialsFactory() implements AwsCredentialsProviderFactory {
|
||||
|
||||
public AwsCredentialsProvider build() {
|
||||
return WebIdentityTokenFileCredentialsProvider.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import com.google.api.gax.batching.BatchingSettings;
|
||||
import com.google.api.gax.batching.FlowControlSettings;
|
||||
import com.google.api.gax.batching.FlowController;
|
||||
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||
import com.google.auth.oauth2.ExternalAccountCredentials;
|
||||
import com.google.cloud.pubsub.v1.Publisher;
|
||||
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||
import com.google.pubsub.v1.TopicName;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record DefaultPubSubPublisherFactory(@NotBlank String project,
|
||||
@NotBlank String topic,
|
||||
@NotBlank String credentialConfiguration) implements
|
||||
PubSubPublisherFactory {
|
||||
|
||||
@Override
|
||||
public PublisherInterface build() throws IOException {
|
||||
|
||||
final FlowControlSettings flowControlSettings = FlowControlSettings.newBuilder()
|
||||
.setLimitExceededBehavior(FlowController.LimitExceededBehavior.ThrowException)
|
||||
.setMaxOutstandingElementCount(100L)
|
||||
.setMaxOutstandingRequestBytes(16 * 1024 * 1024L) // 16MB
|
||||
.build();
|
||||
|
||||
final BatchingSettings batchingSettings = BatchingSettings.newBuilder()
|
||||
.setFlowControlSettings(flowControlSettings)
|
||||
.setDelayThreshold(org.threeten.bp.Duration.ofMillis(10))
|
||||
// These thresholds are actually the default, setting them explicitly since creating a custom batchingSettings resets them
|
||||
.setElementCountThreshold(100L)
|
||||
.setRequestByteThreshold(5000L)
|
||||
.build();
|
||||
|
||||
try (final ByteArrayInputStream credentialConfigInputStream =
|
||||
new ByteArrayInputStream(credentialConfiguration.getBytes(StandardCharsets.UTF_8))) {
|
||||
|
||||
return Publisher.newBuilder(
|
||||
TopicName.of(project, topic))
|
||||
.setCredentialsProvider(
|
||||
FixedCredentialsProvider.create(ExternalAccountCredentials.fromStream(credentialConfigInputStream)))
|
||||
.setBatchingSettings(batchingSettings)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,14 @@
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.micrometer.statsd.StatsdConfig;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.micrometer.statsd.StatsdFlavor;
|
||||
import java.time.Duration;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class DogstatsdConfiguration implements StatsdConfig {
|
||||
@JsonTypeName("default")
|
||||
public class DogstatsdConfiguration implements DatadogConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@@ -31,6 +32,7 @@ public class DogstatsdConfiguration implements StatsdConfig {
|
||||
return step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
@@ -50,4 +52,9 @@ public class DogstatsdConfiguration implements StatsdConfig {
|
||||
public String host() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getShutdownWaitDuration() {
|
||||
return step().plus(step.dividedBy(2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = AppConfigConfiguration.class)
|
||||
public interface DynamicConfigurationManagerFactory extends Discoverable {
|
||||
|
||||
<T> DynamicConfigurationManager<T> build(Class<T> configurationClass,
|
||||
ScheduledExecutorService scheduledExecutorService, AwsCredentialsProvider awsCredentialsProvider);
|
||||
}
|
||||
@@ -9,11 +9,20 @@ import java.time.Duration;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Positive;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
||||
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record DynamoDbClientConfiguration(@NotBlank String region,
|
||||
@NotNull Duration clientExecutionTimeout,
|
||||
@NotNull Duration clientRequestTimeout,
|
||||
@Positive int maxConnections) {
|
||||
@Positive int maxConnections) implements DynamoDbClientFactory {
|
||||
|
||||
public DynamoDbClientConfiguration {
|
||||
if (clientExecutionTimeout == null) {
|
||||
@@ -28,4 +37,32 @@ public record DynamoDbClientConfiguration(@NotBlank String region,
|
||||
maxConnections = 50;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamoDbClient buildSyncClient(final AwsCredentialsProvider credentialsProvider) {
|
||||
return DynamoDbClient.builder()
|
||||
.region(Region.of(region()))
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.apiCallTimeout(clientExecutionTimeout())
|
||||
.apiCallAttemptTimeout(clientRequestTimeout())
|
||||
.build())
|
||||
.httpClientBuilder(AwsCrtHttpClient.builder()
|
||||
.maxConcurrency(maxConnections()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamoDbAsyncClient buildAsyncClient(final AwsCredentialsProvider credentialsProvider) {
|
||||
return DynamoDbAsyncClient.builder()
|
||||
.region(Region.of(region()))
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.apiCallTimeout(clientExecutionTimeout())
|
||||
.apiCallAttemptTimeout(clientRequestTimeout())
|
||||
.build())
|
||||
.httpClientBuilder(NettyNioAsyncHttpClient.builder()
|
||||
.maxConcurrency(maxConnections()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DynamoDbClientConfiguration.class)
|
||||
public interface DynamoDbClientFactory extends Discoverable {
|
||||
|
||||
DynamoDbClient buildSyncClient(AwsCredentialsProvider awsCredentialsProvider);
|
||||
|
||||
DynamoDbAsyncClient buildAsyncClient(AwsCredentialsProvider awsCredentialsProvider);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisClusterConfiguration.class)
|
||||
public interface FaultTolerantRedisClusterFactory extends Discoverable {
|
||||
|
||||
FaultTolerantRedisCluster build(String name, ClientResources.Builder clientResourcesBuilder);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = HCaptchaConfiguration.class)
|
||||
public interface HCaptchaClientFactory extends Discoverable {
|
||||
|
||||
HCaptchaClient build(ScheduledExecutorService retryExecutor,
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager);
|
||||
}
|
||||
@@ -7,9 +7,15 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import org.whispersystems.textsecuregcm.captcha.HCaptchaClient;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class HCaptchaConfiguration {
|
||||
@JsonTypeName("default")
|
||||
public class HCaptchaConfiguration implements HCaptchaClientFactory {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@@ -36,4 +42,14 @@ public class HCaptchaConfiguration {
|
||||
return retry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HCaptchaClient build(final ScheduledExecutorService retryExecutor,
|
||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
|
||||
return new HCaptchaClient(
|
||||
apiKey.value(),
|
||||
retryExecutor,
|
||||
circuitBreaker,
|
||||
retry,
|
||||
dynamicConfigurationManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ public class MessageCacheConfiguration {
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@Valid
|
||||
private RedisClusterConfiguration cluster;
|
||||
private FaultTolerantRedisClusterFactory cluster;
|
||||
|
||||
@JsonProperty
|
||||
private int persistDelayMinutes = 10;
|
||||
|
||||
public RedisClusterConfiguration getRedisClusterConfiguration() {
|
||||
public FaultTolerantRedisClusterFactory getRedisClusterConfiguration() {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,23 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record MonitoredS3ObjectConfiguration(
|
||||
@NotBlank String s3Region,
|
||||
@NotBlank String s3Bucket,
|
||||
@NotBlank String objectKey,
|
||||
Long maxSize,
|
||||
Duration refreshInterval) {
|
||||
Duration refreshInterval) implements S3ObjectMonitorFactory {
|
||||
|
||||
private static long DEFAULT_MAXSIZE = 16*1024*1024;
|
||||
private static Duration DEFAULT_REFRESH_INTERVAL = Duration.ofMinutes(5);
|
||||
private static final long DEFAULT_MAXSIZE = 16 * 1024 * 1024;
|
||||
private static final Duration DEFAULT_REFRESH_INTERVAL = Duration.ofMinutes(5);
|
||||
|
||||
public MonitoredS3ObjectConfiguration {
|
||||
if (maxSize == null) {
|
||||
@@ -26,4 +31,12 @@ public record MonitoredS3ObjectConfiguration(
|
||||
refreshInterval = DEFAULT_REFRESH_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3ObjectMonitor build(final AwsCredentialsProvider awsCredentialsProvider,
|
||||
final ScheduledExecutorService refreshExecutorService) {
|
||||
|
||||
return new S3ObjectMonitor(awsCredentialsProvider, s3Region, s3Bucket, objectKey, maxSize, refreshExecutorService,
|
||||
refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import java.net.http.HttpClient;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record PaymentsServiceClientsConfiguration(@NotNull SecretString coinMarketCapApiKey,
|
||||
@NotNull SecretString fixerApiKey,
|
||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds) implements
|
||||
PaymentsServiceClientsFactory {
|
||||
|
||||
@Override
|
||||
public FixerClient buildFixerClient(final HttpClient httpClient) {
|
||||
return new FixerClient(httpClient, fixerApiKey.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoinMarketCapClient buildCoinMarketCapClient(final HttpClient httpClient) {
|
||||
return new CoinMarketCapClient(httpClient, coinMarketCapApiKey.value(), coinMarketCapCurrencyIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import org.whispersystems.textsecuregcm.currency.CoinMarketCapClient;
|
||||
import org.whispersystems.textsecuregcm.currency.FixerClient;
|
||||
import java.net.http.HttpClient;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = PaymentsServiceClientsConfiguration.class)
|
||||
public interface PaymentsServiceClientsFactory extends Discoverable {
|
||||
|
||||
FixerClient buildFixerClient(final HttpClient httpClient);
|
||||
|
||||
CoinMarketCapClient buildCoinMarketCapClient(HttpClient httpClient);
|
||||
}
|
||||
@@ -5,17 +5,15 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import java.util.List;
|
||||
|
||||
public record PaymentsServiceConfiguration(@NotNull SecretBytes userAuthenticationTokenSharedSecret,
|
||||
@NotNull SecretString coinMarketCapApiKey,
|
||||
@NotNull SecretString fixerApiKey,
|
||||
@NotEmpty Map<@NotBlank String, Integer> coinMarketCapCurrencyIds,
|
||||
@NotEmpty List<String> paymentCurrencies) {
|
||||
@NotEmpty List<String> paymentCurrencies,
|
||||
@NotNull @Valid PaymentsServiceClientsFactory externalClients) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public record ProvisioningConfiguration(@Valid @NotNull SingletonRedisClientFactory pubsub,
|
||||
@Valid @NotNull CircuitBreakerConfiguration circuitBreaker) {
|
||||
|
||||
public ProvisioningConfiguration {
|
||||
if (circuitBreaker == null) {
|
||||
circuitBreaker = new CircuitBreakerConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import java.io.IOException;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultPubSubPublisherFactory.class)
|
||||
public interface PubSubPublisherFactory extends Discoverable {
|
||||
|
||||
PublisherInterface build() throws IOException;
|
||||
}
|
||||
@@ -6,13 +6,17 @@
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import java.time.Duration;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
|
||||
public class RedisClusterConfiguration {
|
||||
@JsonTypeName("default")
|
||||
public class RedisClusterConfiguration implements FaultTolerantRedisClusterFactory {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
@@ -32,6 +36,11 @@ public class RedisClusterConfiguration {
|
||||
@Valid
|
||||
private RetryConfiguration retry = new RetryConfiguration();
|
||||
|
||||
@VisibleForTesting
|
||||
void setConfigurationUri(final String configurationUri) {
|
||||
this.configurationUri = configurationUri;
|
||||
}
|
||||
|
||||
public String getConfigurationUri() {
|
||||
return configurationUri;
|
||||
}
|
||||
@@ -47,4 +56,9 @@ public class RedisClusterConfiguration {
|
||||
public RetryConfiguration getRetryConfiguration() {
|
||||
return retry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaultTolerantRedisCluster build(final String name, final ClientResources.Builder clientResourcesBuilder) {
|
||||
return new FaultTolerantRedisCluster(name, this, clientResourcesBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@ package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisUriUtil;
|
||||
|
||||
public class RedisConfiguration {
|
||||
@JsonTypeName("default")
|
||||
public class RedisConfiguration implements SingletonRedisClientFactory {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
@@ -22,11 +25,6 @@ public class RedisConfiguration {
|
||||
@NotNull
|
||||
private Duration timeout = Duration.ofSeconds(1);
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@Valid
|
||||
private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration();
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
@@ -35,7 +33,12 @@ public class RedisConfiguration {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public CircuitBreakerConfiguration getCircuitBreakerConfiguration() {
|
||||
return circuitBreaker;
|
||||
@Override
|
||||
public RedisClient build(final ClientResources clientResources) {
|
||||
final RedisClient redisClient = RedisClient.create(clientResources,
|
||||
RedisUriUtil.createRedisUriWithTimeout(uri, timeout));
|
||||
redisClient.setDefaultTimeout(timeout);
|
||||
|
||||
return redisClient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.core.setup.Environment;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RegistrationServiceConfiguration.class)
|
||||
public interface RegistrationServiceClientFactory extends Discoverable {
|
||||
|
||||
RegistrationServiceClient build(Environment environment, Executor callbackExecutor,
|
||||
ScheduledExecutorService identityRefreshExecutor);
|
||||
}
|
||||
@@ -1,10 +1,35 @@
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.dropwizard.core.setup.Environment;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import org.whispersystems.textsecuregcm.registration.IdentityTokenCallCredentials;
|
||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||
|
||||
@JsonTypeName("default")
|
||||
public record RegistrationServiceConfiguration(@NotBlank String host,
|
||||
int port,
|
||||
@NotBlank String credentialConfigurationJson,
|
||||
@NotBlank String identityTokenAudience,
|
||||
@NotBlank String registrationCaCertificate) {
|
||||
@NotBlank String registrationCaCertificate) implements
|
||||
RegistrationServiceClientFactory {
|
||||
|
||||
@Override
|
||||
public RegistrationServiceClient build(final Environment environment, final Executor callbackExecutor,
|
||||
final ScheduledExecutorService identityRefreshExecutor) {
|
||||
try {
|
||||
final IdentityTokenCallCredentials callCredentials = IdentityTokenCallCredentials.fromCredentialConfig(
|
||||
credentialConfigurationJson, identityTokenAudience, identityRefreshExecutor);
|
||||
|
||||
environment.lifecycle().manage(callCredentials);
|
||||
|
||||
return new RegistrationServiceClient(host, port, callCredentials, registrationCaCertificate,
|
||||
identityRefreshExecutor);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = MonitoredS3ObjectConfiguration.class)
|
||||
public interface S3ObjectMonitorFactory extends Discoverable {
|
||||
|
||||
S3ObjectMonitor build(AwsCredentialsProvider awsCredentialsProvider,
|
||||
ScheduledExecutorService refreshExecutorService);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.dropwizard.jackson.Discoverable;
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = RedisConfiguration.class)
|
||||
public interface SingletonRedisClientFactory extends Discoverable {
|
||||
|
||||
RedisClient build(ClientResources clientResources);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = StaticAwsCredentialsFactory.class)
|
||||
@JsonTypeName("static")
|
||||
public record StaticAwsCredentialsFactory(@NotNull SecretString accessKeyId,
|
||||
@NotNull SecretString secretAccessKey) implements
|
||||
AwsCredentialsProviderFactory {
|
||||
|
||||
@Override
|
||||
public AwsCredentialsProvider build() {
|
||||
return StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKeyId.value(), secretAccessKey.value()));
|
||||
}
|
||||
}
|
||||
@@ -7,25 +7,24 @@ package org.whispersystems.textsecuregcm.geo;
|
||||
|
||||
import com.maxmind.db.CHMCache;
|
||||
import com.maxmind.geoip2.DatabaseReader;
|
||||
import com.maxmind.geoip2.GeoIp2Provider;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.S3ObjectMonitorFactory;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.s3.S3ObjectMonitor;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
|
||||
public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed {
|
||||
|
||||
@@ -39,21 +38,11 @@ public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MaxMindDatabaseManager.class);
|
||||
|
||||
public MaxMindDatabaseManager(
|
||||
@Nonnull final ScheduledExecutorService executorService,
|
||||
@Nonnull final MonitoredS3ObjectConfiguration configuration,
|
||||
@Nonnull final String databaseTag
|
||||
){
|
||||
this.databaseMonitor = new S3ObjectMonitor(
|
||||
configuration.s3Region(),
|
||||
configuration.s3Bucket(),
|
||||
configuration.objectKey(),
|
||||
configuration.maxSize(),
|
||||
executorService,
|
||||
configuration.refreshInterval(),
|
||||
this::handleDatabaseChanged
|
||||
);
|
||||
public MaxMindDatabaseManager(final ScheduledExecutorService executorService,
|
||||
final AwsCredentialsProvider awsCredentialsProvider, final S3ObjectMonitorFactory configuration,
|
||||
final String databaseTag) {
|
||||
|
||||
this.databaseMonitor = configuration.build(awsCredentialsProvider, executorService);
|
||||
this.databaseTag = databaseTag;
|
||||
this.refreshTimer = Metrics.timer(MetricsUtil.name(MaxMindDatabaseManager.class, "refresh"), "db", databaseTag);
|
||||
}
|
||||
@@ -93,17 +82,15 @@ public class MaxMindDatabaseManager implements Supplier<DatabaseReader>, Managed
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
Managed.super.start();
|
||||
databaseMonitor.start();
|
||||
databaseMonitor.start(this::handleDatabaseChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
Managed.super.stop();
|
||||
databaseMonitor.stop();
|
||||
|
||||
final DatabaseReader reader = databaseReader.getAndSet(null);
|
||||
if(reader != null) {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ public class MetricsUtil {
|
||||
|
||||
environment.lifecycle().addEventListener(new ApplicationShutdownMonitor(Metrics.globalRegistry));
|
||||
environment.lifecycle().addEventListener(
|
||||
new MicrometerRegistryManager(Metrics.globalRegistry, config.getDatadogConfiguration().pollingFrequency()));
|
||||
new MicrometerRegistryManager(Metrics.globalRegistry,
|
||||
config.getDatadogConfiguration().getShutdownWaitDuration()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -14,14 +14,13 @@ import org.slf4j.LoggerFactory;
|
||||
public class MicrometerRegistryManager implements LifeCycle.Listener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MicrometerRegistryManager.class);
|
||||
private static final Duration BUFFER = Duration.ofSeconds(5);
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final Duration waitDuration;
|
||||
|
||||
public MicrometerRegistryManager(final MeterRegistry meterRegistry, final Duration pollingFrequency) {
|
||||
public MicrometerRegistryManager(final MeterRegistry meterRegistry, final Duration waitDuration) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.waitDuration = pollingFrequency.plus(BUFFER);
|
||||
this.waitDuration = waitDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
@@ -17,11 +16,9 @@ import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.codec.ByteArrayCodec;
|
||||
import io.lettuce.core.pubsub.RedisPubSubAdapter;
|
||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
import io.lettuce.core.resource.ClientResources;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
@@ -29,7 +26,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisOperation;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisUriUtil;
|
||||
import org.whispersystems.textsecuregcm.storage.PubSubProtos;
|
||||
import org.whispersystems.textsecuregcm.util.CircuitBreakerUtil;
|
||||
import org.whispersystems.textsecuregcm.websocket.InvalidWebsocketAddressException;
|
||||
@@ -56,22 +52,10 @@ public class ProvisioningManager extends RedisPubSubAdapter<byte[], byte[]> impl
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
|
||||
|
||||
public ProvisioningManager(final String redisUri,
|
||||
final ClientResources clientResources,
|
||||
final Duration timeout,
|
||||
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
|
||||
|
||||
this(RedisClient.create(clientResources, RedisUriUtil.createRedisUriWithTimeout(redisUri, timeout)), timeout,
|
||||
circuitBreakerConfiguration);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ProvisioningManager(final RedisClient redisClient,
|
||||
final Duration timeout,
|
||||
public ProvisioningManager(final RedisClient redisClient,
|
||||
final CircuitBreakerConfiguration circuitBreakerConfiguration) {
|
||||
|
||||
this.redisClient = redisClient;
|
||||
this.redisClient.setDefaultTimeout(timeout);
|
||||
|
||||
this.subscriptionConnection = redisClient.connectPubSub(new ByteArrayCodec());
|
||||
this.publicationConnection = redisClient.connect(new ByteArrayCodec());
|
||||
|
||||
@@ -7,13 +7,13 @@ package org.whispersystems.textsecuregcm.registration;
|
||||
import com.google.auth.oauth2.ExternalAccountCredentials;
|
||||
import com.google.auth.oauth2.ImpersonatedCredentials;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.github.resilience4j.core.IntervalFunction;
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.Metadata;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
@@ -28,7 +28,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class IdentityTokenCallCredentials extends CallCredentials implements Closeable {
|
||||
public class IdentityTokenCallCredentials extends CallCredentials implements Managed {
|
||||
private static final Duration IDENTITY_TOKEN_LIFETIME = Duration.ofHours(1);
|
||||
private static final Duration IDENTITY_TOKEN_REFRESH_BUFFER = Duration.ofMinutes(10);
|
||||
|
||||
@@ -58,7 +58,7 @@ class IdentityTokenCallCredentials extends CallCredentials implements Closeable
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
static IdentityTokenCallCredentials fromCredentialConfig(
|
||||
public static IdentityTokenCallCredentials fromCredentialConfig(
|
||||
final String credentialConfigJson,
|
||||
final String audience,
|
||||
final ScheduledExecutorService scheduledExecutorService) throws IOException {
|
||||
@@ -129,7 +129,7 @@ class IdentityTokenCallCredentials extends CallCredentials implements Closeable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
public void stop() {
|
||||
synchronized (this) {
|
||||
if (!scheduledFuture.isDone()) {
|
||||
scheduledFuture.cancel(true);
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.Deadline;
|
||||
import io.grpc.Grpc;
|
||||
@@ -21,7 +22,6 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
@@ -38,7 +38,6 @@ import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||
public class RegistrationServiceClient implements Managed {
|
||||
|
||||
private final ManagedChannel channel;
|
||||
private final IdentityTokenCallCredentials identityTokenCallCredentials;
|
||||
private final RegistrationServiceGrpc.RegistrationServiceFutureStub stub;
|
||||
private final Executor callbackExecutor;
|
||||
|
||||
@@ -61,11 +60,9 @@ public class RegistrationServiceClient implements Managed {
|
||||
|
||||
public RegistrationServiceClient(final String host,
|
||||
final int port,
|
||||
final String credentialConfigJson,
|
||||
final String identityTokenAudience,
|
||||
final CallCredentials callCredentials,
|
||||
final String caCertificatePem,
|
||||
final Executor callbackExecutor,
|
||||
final ScheduledExecutorService identityRefreshExecutor) throws IOException {
|
||||
final Executor callbackExecutor) throws IOException {
|
||||
|
||||
try (final ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(caCertificatePem.getBytes(StandardCharsets.UTF_8))) {
|
||||
final ChannelCredentials tlsChannelCredentials = TlsChannelCredentials.newBuilder()
|
||||
@@ -77,10 +74,7 @@ public class RegistrationServiceClient implements Managed {
|
||||
.build();
|
||||
}
|
||||
|
||||
this.identityTokenCallCredentials = IdentityTokenCallCredentials.fromCredentialConfig(
|
||||
credentialConfigJson, identityTokenAudience, identityRefreshExecutor);
|
||||
|
||||
this.stub = RegistrationServiceGrpc.newFutureStub(channel).withCallCredentials(identityTokenCallCredentials);
|
||||
this.stub = RegistrationServiceGrpc.newFutureStub(channel).withCallCredentials(callCredentials);
|
||||
|
||||
this.callbackExecutor = callbackExecutor;
|
||||
}
|
||||
@@ -284,6 +278,5 @@ public class RegistrationServiceClient implements Managed {
|
||||
if (channel != null) {
|
||||
channel.shutdown();
|
||||
}
|
||||
this.identityTokenCallCredentials.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.s3;
|
||||
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ManagedSupplier<T> extends Supplier<T>, Managed {
|
||||
|
||||
@Override
|
||||
default void start() throws Exception {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
default void stop() throws Exception {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.s3;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.MonitoredS3ObjectConfiguration;
|
||||
|
||||
public class S3MonitoringSupplier<T> implements ManagedSupplier<T> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Nonnull
|
||||
private final Timer refreshTimer;
|
||||
|
||||
@Nonnull
|
||||
private final Counter refreshErrors;
|
||||
|
||||
@Nonnull
|
||||
private final AtomicReference<T> holder;
|
||||
|
||||
@Nonnull
|
||||
private final S3ObjectMonitor monitor;
|
||||
|
||||
@Nonnull
|
||||
private final Function<InputStream, T> parser;
|
||||
|
||||
|
||||
public S3MonitoringSupplier(
|
||||
@Nonnull final ScheduledExecutorService executor,
|
||||
@Nonnull final MonitoredS3ObjectConfiguration cfg,
|
||||
@Nonnull final Function<InputStream, T> parser,
|
||||
@Nonnull final T initial,
|
||||
@Nonnull final String name) {
|
||||
this.refreshTimer = Metrics.timer(name(name, "refresh"));
|
||||
this.refreshErrors = Metrics.counter(name(name, "refreshErrors"));
|
||||
this.holder = new AtomicReference<>(initial);
|
||||
this.parser = requireNonNull(parser);
|
||||
this.monitor = new S3ObjectMonitor(
|
||||
cfg.s3Region(),
|
||||
cfg.s3Bucket(),
|
||||
cfg.objectKey(),
|
||||
cfg.maxSize(),
|
||||
executor,
|
||||
cfg.refreshInterval(),
|
||||
this::handleObjectChange
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public T get() {
|
||||
return requireNonNull(holder.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
monitor.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
monitor.stop();
|
||||
}
|
||||
|
||||
private void handleObjectChange(@Nonnull final InputStream inputStream) {
|
||||
refreshTimer.record(() -> {
|
||||
// parser function is supposed to close the input stream
|
||||
try {
|
||||
holder.set(parser.apply(inputStream));
|
||||
} catch (final Exception e) {
|
||||
log.error("failed to update internal state from the monitored object", e);
|
||||
refreshErrors.increment();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import java.util.function.Consumer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.core.ResponseInputStream;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
@@ -39,8 +39,6 @@ public class S3ObjectMonitor {
|
||||
private final Duration refreshInterval;
|
||||
private ScheduledFuture<?> refreshFuture;
|
||||
|
||||
private final Consumer<InputStream> changeListener;
|
||||
|
||||
private final AtomicReference<String> lastETag = new AtomicReference<>();
|
||||
|
||||
private final S3Client s3Client;
|
||||
@@ -48,24 +46,23 @@ public class S3ObjectMonitor {
|
||||
private static final Logger log = LoggerFactory.getLogger(S3ObjectMonitor.class);
|
||||
|
||||
public S3ObjectMonitor(
|
||||
final AwsCredentialsProvider awsCredentialsProvider,
|
||||
final String s3Region,
|
||||
final String s3Bucket,
|
||||
final String objectKey,
|
||||
final long maxObjectSize,
|
||||
final ScheduledExecutorService refreshExecutorService,
|
||||
final Duration refreshInterval,
|
||||
final Consumer<InputStream> changeListener) {
|
||||
final Duration refreshInterval) {
|
||||
|
||||
this(S3Client.builder()
|
||||
.region(Region.of(s3Region))
|
||||
.credentialsProvider(WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER)
|
||||
.credentialsProvider(awsCredentialsProvider)
|
||||
.build(),
|
||||
s3Bucket,
|
||||
objectKey,
|
||||
maxObjectSize,
|
||||
refreshExecutorService,
|
||||
refreshInterval,
|
||||
changeListener);
|
||||
refreshInterval);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -75,8 +72,7 @@ public class S3ObjectMonitor {
|
||||
final String objectKey,
|
||||
final long maxObjectSize,
|
||||
final ScheduledExecutorService refreshExecutorService,
|
||||
final Duration refreshInterval,
|
||||
final Consumer<InputStream> changeListener) {
|
||||
final Duration refreshInterval) {
|
||||
|
||||
this.s3Client = s3Client;
|
||||
this.s3Bucket = s3Bucket;
|
||||
@@ -85,21 +81,19 @@ public class S3ObjectMonitor {
|
||||
|
||||
this.refreshExecutorService = refreshExecutorService;
|
||||
this.refreshInterval = refreshInterval;
|
||||
|
||||
this.changeListener = changeListener;
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
public synchronized void start(final Consumer<InputStream> changeListener) {
|
||||
if (refreshFuture != null) {
|
||||
throw new RuntimeException("S3 object manager already started");
|
||||
}
|
||||
|
||||
// Run the first request immediately/blocking, then start subsequent calls.
|
||||
log.info("Initial request for s3://{}/{}", s3Bucket, objectKey);
|
||||
refresh();
|
||||
refresh(changeListener);
|
||||
|
||||
refreshFuture = refreshExecutorService
|
||||
.scheduleAtFixedRate(this::refresh, refreshInterval.toMillis(), refreshInterval.toMillis(),
|
||||
.scheduleAtFixedRate(() -> refresh(changeListener), refreshInterval.toMillis(), refreshInterval.toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@@ -139,7 +133,7 @@ public class S3ObjectMonitor {
|
||||
* changed since the last call to {@link #getObject()} or {@code refresh()}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void refresh() {
|
||||
void refresh(final Consumer<InputStream> changeListener) {
|
||||
try {
|
||||
final HeadObjectResponse objectMetadata = s3Client.headObject(HeadObjectRequest.builder()
|
||||
.bucket(s3Bucket)
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||
import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
|
||||
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest;
|
||||
@@ -54,9 +55,11 @@ public class DynamicConfigurationManager<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicConfigurationManager.class);
|
||||
|
||||
public DynamicConfigurationManager(String application, String environment, String configurationName,
|
||||
Class<T> configurationClass, ScheduledExecutorService scheduledExecutorService) {
|
||||
AwsCredentialsProvider awsCredentialsProvider, Class<T> configurationClass,
|
||||
ScheduledExecutorService scheduledExecutorService) {
|
||||
this(AppConfigDataClient
|
||||
.builder()
|
||||
.credentialsProvider(awsCredentialsProvider)
|
||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.apiCallTimeout(Duration.ofSeconds(10))
|
||||
.apiCallAttemptTimeout(Duration.ofSeconds(10)).build())
|
||||
@@ -86,6 +89,9 @@ public class DynamicConfigurationManager<T> {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (initialized.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
configuration.set(retrieveInitialDynamicConfiguration());
|
||||
initialized.countDown();
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@ import com.braintreegateway.TransactionSearchRequest;
|
||||
import com.braintreegateway.exceptions.BraintreeException;
|
||||
import com.braintreegateway.exceptions.NotFoundException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.cloud.pubsub.v1.Publisher;
|
||||
import com.google.cloud.pubsub.v1.PublisherInterface;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.pubsub.v1.PubsubMessage;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -40,8 +42,6 @@ import javax.annotation.Nullable;
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import com.google.pubsub.v1.PubsubMessage;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
@@ -65,7 +65,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
||||
private final BraintreeGateway braintreeGateway;
|
||||
private final BraintreeGraphqlClient braintreeGraphqlClient;
|
||||
private final CurrencyConversionManager currencyConversionManager;
|
||||
private final Publisher pubsubPublisher;
|
||||
private final PublisherInterface pubsubPublisher;
|
||||
private final Executor executor;
|
||||
private final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod;
|
||||
private final Map<String, String> currenciesToMerchantAccounts;
|
||||
@@ -79,7 +79,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
||||
final Map<String, String> currenciesToMerchantAccounts,
|
||||
final String graphqlUri,
|
||||
final CurrencyConversionManager currencyConversionManager,
|
||||
final Publisher pubsubPublisher,
|
||||
final PublisherInterface pubsubPublisher,
|
||||
final CircuitBreakerConfiguration circuitBreakerConfiguration,
|
||||
final Executor executor,
|
||||
final ScheduledExecutorService retryExecutor) {
|
||||
@@ -107,7 +107,7 @@ public class BraintreeManager implements SubscriptionProcessorManager {
|
||||
BraintreeManager(final BraintreeGateway braintreeGateway,
|
||||
final Map<PaymentMethod, Set<String>> supportedCurrenciesByPaymentMethod,
|
||||
final Map<String, String> currenciesToMerchantAccounts, final BraintreeGraphqlClient braintreeGraphqlClient,
|
||||
final CurrencyConversionManager currencyConversionManager, final Publisher pubsubPublisher,
|
||||
final CurrencyConversionManager currencyConversionManager, final PublisherInterface pubsubPublisher,
|
||||
final Executor executor) {
|
||||
this.braintreeGateway = braintreeGateway;
|
||||
this.supportedCurrenciesByPaymentMethod = supportedCurrenciesByPaymentMethod;
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
|
||||
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
|
||||
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
|
||||
public class DynamoDbFromConfig {
|
||||
|
||||
public static DynamoDbClient client(DynamoDbClientConfiguration config, AwsCredentialsProvider credentialsProvider) {
|
||||
return DynamoDbClient.builder()
|
||||
.region(Region.of(config.region()))
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.apiCallTimeout(config.clientExecutionTimeout())
|
||||
.apiCallAttemptTimeout(config.clientRequestTimeout())
|
||||
.build())
|
||||
.httpClientBuilder(AwsCrtHttpClient
|
||||
.builder()
|
||||
.maxConcurrency(config.maxConnections()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static DynamoDbAsyncClient asyncClient(
|
||||
DynamoDbClientConfiguration config,
|
||||
AwsCredentialsProvider credentialsProvider) {
|
||||
return DynamoDbAsyncClient.builder()
|
||||
.region(Region.of(config.region()))
|
||||
.credentialsProvider(credentialsProvider)
|
||||
.overrideConfiguration(ClientOverrideConfiguration.builder()
|
||||
.apiCallTimeout(config.clientExecutionTimeout())
|
||||
.apiCallAttemptTimeout(config.clientRequestTimeout())
|
||||
.build())
|
||||
.httpClientBuilder(NettyNioAsyncHttpClient.builder()
|
||||
.maxConcurrency(config.maxConnections()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.SecureStorageController;
|
||||
@@ -50,10 +49,10 @@ import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||
import org.whispersystems.textsecuregcm.storage.UsernameHashNotAvailableException;
|
||||
import org.whispersystems.textsecuregcm.storage.UsernameReservationNotFoundException;
|
||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
|
||||
@@ -97,18 +96,19 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
||||
throws Exception {
|
||||
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
final AwsCredentialsProvider awsCredentialsProvider = configuration.getAwsCredentialsConfiguration().build();
|
||||
|
||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||
.scheduledExecutorService(name(getClass(), "dynamicConfiguration-%d")).threads(1).build();
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class, dynamicConfigurationExecutor);
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = configuration.getAppConfig().build(
|
||||
DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||
dynamicConfigurationManager.start();
|
||||
|
||||
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
||||
|
||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache_cluster",
|
||||
configuration.getCacheClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster cacheCluster = configuration.getCacheClusterConfiguration().build("main_cache_cluster",
|
||||
redisClientResourcesBuilder);
|
||||
|
||||
Scheduler messageDeliveryScheduler = Schedulers.fromExecutorService(
|
||||
environment.lifecycle().executorService("messageDelivery-%d").maxThreads(4)
|
||||
@@ -135,11 +135,11 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
||||
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||
configuration.getSvr2Configuration());
|
||||
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = configuration.getDynamoDbClientConfiguration()
|
||||
.buildAsyncClient(awsCredentialsProvider);
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(configuration.getDynamoDbClientConfiguration(),
|
||||
WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbClient dynamoDbClient = configuration.getDynamoDbClientConfiguration()
|
||||
.buildSyncClient(awsCredentialsProvider);
|
||||
|
||||
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
||||
@@ -173,12 +173,12 @@ public class AssignUsernameCommand extends EnvironmentCommand<WhisperServerConfi
|
||||
configuration.getDynamoDbTables().getMessages().getTableName(),
|
||||
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
||||
messageDeletionExecutor);
|
||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
||||
configuration.getMessageCacheConfiguration().getRedisClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
||||
configuration.getClientPresenceClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
||||
configuration.getRateLimitersCluster(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster messagesCluster = configuration.getMessageCacheConfiguration()
|
||||
.getRedisClusterConfiguration().build("messages", redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster clientPresenceCluster = configuration.getClientPresenceClusterConfiguration()
|
||||
.build("client_presence", redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster rateLimitersCluster = configuration.getRateLimitersCluster().build("rate_limiters",
|
||||
redisClientResourcesBuilder);
|
||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
||||
secureValueRecoveryCredentialsGenerator, secureValueRecoveryExecutor, secureValueRecoveryServiceRetryExecutor,
|
||||
configuration.getSvr2Configuration());
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.signal.libsignal.zkgroup.GenericServerSecretParams;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerService;
|
||||
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
|
||||
import org.whispersystems.textsecuregcm.backup.BackupManager;
|
||||
@@ -49,10 +48,10 @@ import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswords;
|
||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ReportMessageDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||
import org.whispersystems.textsecuregcm.util.DynamoDbFromConfig;
|
||||
import org.whispersystems.textsecuregcm.util.ManagedAwsCrt;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||
|
||||
@@ -80,20 +79,21 @@ record CommandDependencies(
|
||||
|
||||
environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
final AwsCredentialsProvider awsCredentialsProvider = configuration.getAwsCredentialsConfiguration().build();
|
||||
|
||||
ScheduledExecutorService dynamicConfigurationExecutor = environment.lifecycle()
|
||||
.scheduledExecutorService(name(name, "dynamicConfiguration-%d")).threads(1).build();
|
||||
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = new DynamicConfigurationManager<>(
|
||||
configuration.getAppConfig().getApplication(), configuration.getAppConfig().getEnvironment(),
|
||||
configuration.getAppConfig().getConfigurationName(), DynamicConfiguration.class, dynamicConfigurationExecutor);
|
||||
DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager = configuration.getAppConfig().build(
|
||||
DynamicConfiguration.class, dynamicConfigurationExecutor, awsCredentialsProvider);
|
||||
dynamicConfigurationManager.start();
|
||||
|
||||
MetricsUtil.configureRegistries(configuration, environment, dynamicConfigurationManager);
|
||||
|
||||
final ClientResources.Builder redisClientResourcesBuilder = ClientResources.builder();
|
||||
|
||||
FaultTolerantRedisCluster cacheCluster = new FaultTolerantRedisCluster("main_cache",
|
||||
configuration.getCacheClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster cacheCluster = configuration.getCacheClusterConfiguration().build("main_cache",
|
||||
redisClientResourcesBuilder);
|
||||
|
||||
ScheduledExecutorService recurringJobExecutor = environment.lifecycle()
|
||||
.scheduledExecutorService(name(name, "recurringJob-%d")).threads(2).build();
|
||||
@@ -124,11 +124,11 @@ record CommandDependencies(
|
||||
ExternalServiceCredentialsGenerator secureValueRecoveryCredentialsGenerator = SecureValueRecovery2Controller.credentialsGenerator(
|
||||
configuration.getSvr2Configuration());
|
||||
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbFromConfig.asyncClient(
|
||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbAsyncClient dynamoDbAsyncClient = configuration.getDynamoDbClientConfiguration()
|
||||
.buildAsyncClient(awsCredentialsProvider);
|
||||
|
||||
DynamoDbClient dynamoDbClient = DynamoDbFromConfig.client(
|
||||
configuration.getDynamoDbClientConfiguration(), WhisperServerService.AWSSDK_CREDENTIALS_PROVIDER);
|
||||
DynamoDbClient dynamoDbClient = configuration.getDynamoDbClientConfiguration()
|
||||
.buildSyncClient(awsCredentialsProvider);
|
||||
|
||||
RegistrationRecoveryPasswords registrationRecoveryPasswords = new RegistrationRecoveryPasswords(
|
||||
configuration.getDynamoDbTables().getRegistrationRecovery().getTableName(),
|
||||
@@ -163,12 +163,12 @@ record CommandDependencies(
|
||||
configuration.getDynamoDbTables().getMessages().getTableName(),
|
||||
configuration.getDynamoDbTables().getMessages().getExpiration(),
|
||||
messageDeletionExecutor);
|
||||
FaultTolerantRedisCluster messagesCluster = new FaultTolerantRedisCluster("messages",
|
||||
configuration.getMessageCacheConfiguration().getRedisClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster clientPresenceCluster = new FaultTolerantRedisCluster("client_presence",
|
||||
configuration.getClientPresenceClusterConfiguration(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster rateLimitersCluster = new FaultTolerantRedisCluster("rate_limiters",
|
||||
configuration.getRateLimitersCluster(), redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster messagesCluster = configuration.getMessageCacheConfiguration()
|
||||
.getRedisClusterConfiguration().build("messages", redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster clientPresenceCluster = configuration.getClientPresenceClusterConfiguration()
|
||||
.build("client_presence", redisClientResourcesBuilder);
|
||||
FaultTolerantRedisCluster rateLimitersCluster = configuration.getRateLimitersCluster().build("rate_limiters",
|
||||
redisClientResourcesBuilder);
|
||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(
|
||||
secureValueRecoveryCredentialsGenerator, secureValueRecoveryServiceExecutor,
|
||||
secureValueRecoveryServiceRetryExecutor,
|
||||
|
||||
@@ -63,8 +63,8 @@ public class ScheduledApnPushNotificationSenderServiceCommand extends ServerComm
|
||||
});
|
||||
}
|
||||
|
||||
final FaultTolerantRedisCluster pushSchedulerCluster = new FaultTolerantRedisCluster("push_scheduler",
|
||||
configuration.getPushSchedulerCluster(), deps.redisClusterClientResourcesBuilder());
|
||||
final FaultTolerantRedisCluster pushSchedulerCluster = configuration.getPushSchedulerCluster()
|
||||
.build("push_scheduler", deps.redisClusterClientResourcesBuilder());
|
||||
|
||||
final ExecutorService apnSenderExecutor = environment.lifecycle().executorService(name(getClass(), "apnSender-%d"))
|
||||
.maxThreads(1).minThreads(1).build();
|
||||
|
||||
Reference in New Issue
Block a user