From 689fa2154375bd962e78d1903ad64b3ed44c9570 Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Sun, 13 Feb 2022 22:50:05 +0100 Subject: [PATCH 1/4] Migration S3 to affix settings All S3 settings have such prefix: aive.s3.*. - Pass Map of S3ClientSettings - EncryptionKeyProvider now creates/recreates together with S3 client --- .../repositories/s3/S3ClientSettings.java | 138 ++++++++++++------ .../repositories/s3/S3RepositoryPlugin.java | 4 +- .../s3/S3RepositoryStorageIOProvider.java | 6 +- .../repositories/s3/S3SettingsProvider.java | 5 +- .../repositories/s3/S3ClientProviderTest.java | 105 +++++++++---- .../repositories/s3/S3ClientSettingsTest.java | 118 ++++++++++----- .../repositories/s3/S3StorageIOTest.java | 4 +- 7 files changed, 255 insertions(+), 125 deletions(-) diff --git a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettings.java b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettings.java index 30d87ca..58714b0 100644 --- a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettings.java +++ b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettings.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import org.opensearch.common.settings.SecureSetting; import org.opensearch.common.settings.SecureString; @@ -32,43 +35,72 @@ import com.amazonaws.auth.BasicAWSCredentials; import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.checkSettings; +import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.getConfigValue; import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.readInputStream; -import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.withPrefix; public class S3ClientSettings implements ClientSettings { - public static final Setting PUBLIC_KEY_FILE = - SecureSetting.secureFile(withPrefix("s3.public_key_file"), null); - - public static final Setting PRIVATE_KEY_FILE = - SecureSetting.secureFile(withPrefix("s3.private_key_file"), null); - - public static final Setting AWS_SECRET_ACCESS_KEY = - SecureSetting.secureString(withPrefix("s3.client.aws_secret_access_key"), null); - - public static final Setting AWS_ACCESS_KEY_ID = - SecureSetting.secureString(withPrefix("s3.client.aws_access_key_id"), null); - - public static final Setting ENDPOINT = - SecureSetting.secureString(withPrefix("s3.client.endpoint"), null); - - public static final Setting MAX_RETRIES = - Setting.intSetting( - withPrefix("s3.client.max_retries"), - ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry(), - Setting.Property.NodeScope); - - public static final Setting USE_THROTTLE_RETRIES = - Setting.boolSetting( - withPrefix("s3.client.use_throttle_retries"), - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, - Setting.Property.NodeScope); - - public static final Setting READ_TIMEOUT = - Setting.timeSetting( - withPrefix("s3.client.read_timeout"), - TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), - Setting.Property.NodeScope); + static final String S3_PREFIX = AIVEN_PREFIX + "s3."; + + public static final Setting.AffixSetting PUBLIC_KEY_FILE = + Setting.affixKeySetting( + S3_PREFIX, + "public_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting PRIVATE_KEY_FILE = + Setting.affixKeySetting( + S3_PREFIX, + "private_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting AWS_SECRET_ACCESS_KEY = + Setting.affixKeySetting( + S3_PREFIX, + "client.aws_secret_access_key", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting AWS_ACCESS_KEY_ID = + Setting.affixKeySetting( + S3_PREFIX, + "client.aws_access_key_id", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting ENDPOINT = + Setting.affixKeySetting( + S3_PREFIX, + "client.endpoint", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting MAX_RETRIES = + Setting.affixKeySetting( + S3_PREFIX, + "client.max_retries", + key -> Setting.intSetting(key, + ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry(), Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting USE_THROTTLE_RETRIES = + Setting.affixKeySetting( + S3_PREFIX, + "client.use_throttle_retries", + key -> Setting.boolSetting(key, + ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting READ_TIMEOUT = + Setting.affixKeySetting( + S3_PREFIX, + "client.read_timeout", + key -> Setting.timeSetting(key, + TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), + Setting.Property.NodeScope) + ); private final byte[] publicKey; @@ -101,10 +133,12 @@ private S3ClientSettings( this.readTimeout = readTimeout; } + @Override public byte[] publicKey() { return publicKey; } + @Override public byte[] privateKey() { return privateKey; } @@ -129,26 +163,36 @@ public int readTimeout() { return Math.toIntExact(readTimeout); } - public static S3ClientSettings create(final Settings settings) throws IOException { + public static Map create(final Settings settings) throws IOException { if (settings.isEmpty()) { throw new IllegalArgumentException("Settings for AWS S3 haven't been set"); } - checkSettings(AWS_ACCESS_KEY_ID, settings); - checkSettings(AWS_SECRET_ACCESS_KEY, settings); - checkSettings(ENDPOINT, settings); - checkSettings(PUBLIC_KEY_FILE, settings); - checkSettings(PRIVATE_KEY_FILE, settings); + final Set clientNames = settings.getGroups(S3_PREFIX).keySet(); + final var clientSettings = new HashMap(); + for (final var clientName : clientNames) { + clientSettings.put(clientName, createSettings(clientName, settings)); + } + return Map.copyOf(clientSettings); + } + + static S3ClientSettings createSettings(final String clientName, final Settings settings) throws IOException { + checkSettings(AWS_ACCESS_KEY_ID, clientName, settings); + checkSettings(AWS_SECRET_ACCESS_KEY, clientName, settings); + checkSettings(ENDPOINT, clientName, settings); + checkSettings(PUBLIC_KEY_FILE, clientName, settings); + checkSettings(PRIVATE_KEY_FILE, clientName, settings); return new S3ClientSettings( - readInputStream(PUBLIC_KEY_FILE, settings), - readInputStream(PRIVATE_KEY_FILE, settings), + readInputStream(getConfigValue(settings, clientName, PUBLIC_KEY_FILE)), + readInputStream(getConfigValue(settings, clientName, PRIVATE_KEY_FILE)), new BasicAWSCredentials( - AWS_ACCESS_KEY_ID.get(settings).toString(), - AWS_SECRET_ACCESS_KEY.get(settings).toString() + getConfigValue(settings, clientName, AWS_ACCESS_KEY_ID).toString(), + getConfigValue(settings, clientName, AWS_SECRET_ACCESS_KEY).toString() ), - ENDPOINT.get(settings).toString(), - MAX_RETRIES.get(settings), - USE_THROTTLE_RETRIES.get(settings), - READ_TIMEOUT.get(settings).millis()); + getConfigValue(settings, clientName, ENDPOINT).toString(), + getConfigValue(settings, clientName, MAX_RETRIES), + getConfigValue(settings, clientName, USE_THROTTLE_RETRIES), + getConfigValue(settings, clientName, READ_TIMEOUT).millis() + ); } } diff --git a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryPlugin.java index 8f83f59..ce1a92a 100644 --- a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -28,12 +28,14 @@ import com.amazonaws.services.s3.AmazonS3Client; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.S3_PREFIX; + public class S3RepositoryPlugin extends AbstractRepositoryPlugin { public static final String REPOSITORY_TYPE = "aiven-s3"; public S3RepositoryPlugin(final Settings settings) { - super(REPOSITORY_TYPE, settings, new S3SettingsProvider()); + super(REPOSITORY_TYPE, S3_PREFIX, settings, new S3SettingsProvider()); } @Override diff --git a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryStorageIOProvider.java b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryStorageIOProvider.java index 3083a7d..6717bc2 100644 --- a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryStorageIOProvider.java +++ b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3RepositoryStorageIOProvider.java @@ -35,7 +35,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; import io.aiven.elasticsearch.repositories.io.CryptoIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.amazonaws.AmazonClientException; import com.amazonaws.services.s3.AmazonS3Client; @@ -64,9 +63,8 @@ public class S3RepositoryStorageIOProvider extends RepositoryStorageIOProvider clientSettings) { + super(new S3ClientProvider(), clientSettings); } @Override diff --git a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3SettingsProvider.java b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3SettingsProvider.java index 97ad751..3e80400 100644 --- a/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3SettingsProvider.java +++ b/repository-s3/src/main/java/io/aiven/elasticsearch/repositories/s3/S3SettingsProvider.java @@ -23,7 +23,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositorySettingsProvider; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.amazonaws.services.s3.AmazonS3Client; @@ -34,9 +33,7 @@ protected RepositoryStorageIOProvider createRe final Settings settings) throws IOException { return Permissions.doPrivileged(() -> { final var s3ClientSettings = S3ClientSettings.create(settings); - final var encryptionKeyProvider = - EncryptionKeyProvider.of(s3ClientSettings.publicKey(), s3ClientSettings.privateKey()); - return new S3RepositoryStorageIOProvider(s3ClientSettings, encryptionKeyProvider); + return new S3RepositoryStorageIOProvider(s3ClientSettings); }); } diff --git a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientProviderTest.java b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientProviderTest.java index 1dcfc94..30dbaa8 100644 --- a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientProviderTest.java +++ b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientProviderTest.java @@ -33,6 +33,14 @@ import org.junit.platform.commons.support.HierarchyTraversalMode; import org.junit.platform.commons.support.ReflectionSupport; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.AWS_ACCESS_KEY_ID; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.AWS_SECRET_ACCESS_KEY; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.ENDPOINT; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.MAX_RETRIES; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.PRIVATE_KEY_FILE; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.PUBLIC_KEY_FILE; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.READ_TIMEOUT; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.USE_THROTTLE_RETRIES; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -52,17 +60,29 @@ void providerInitialization() throws Exception { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "http://endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString( + AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID" + ).setString( + AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString( + ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "http://endpoint" + ).setFile( + PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ).setFile( + PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); final var settings = Settings.builder() - .put(S3ClientSettings.MAX_RETRIES.getKey(), 12) - .put(S3ClientSettings.READ_TIMEOUT.getKey(), TimeValue.timeValueMillis(1000L)) - .put(S3ClientSettings.USE_THROTTLE_RETRIES.getKey(), false) + .put(MAX_RETRIES.getConcreteSettingForNamespace("default").getKey(), 12) + .put(READ_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), + TimeValue.timeValueMillis(1000L)) + .put(USE_THROTTLE_RETRIES.getConcreteSettingForNamespace("default").getKey(), false) .setSecureSettings(secureSettings) .build(); final var repoSettings = @@ -71,7 +91,11 @@ void providerInitialization() throws Exception { .put("some_settings_2", 210) .build(); - final var client = s3ClientProvider.buildClientIfNeeded(S3ClientSettings.create(settings), repoSettings); + final var client = + s3ClientProvider.buildClientIfNeeded( + S3ClientSettings.create(settings).get("default"), + repoSettings + ).v2(); final var amazonS3Client = (AmazonS3Client) client; assertEquals(S3ClientProvider.HTTP_USER_AGENT, amazonS3Client.getClientConfiguration().getUserAgentPrefix()); @@ -88,11 +112,16 @@ void providerInitializationWithDefaultValues() throws Exception { final var s3ClientProvider = new S3ClientProvider(); final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "http://endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "http://endpoint") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var settings = Settings.builder().setSecureSettings(secureSettings).build(); @@ -103,7 +132,11 @@ void providerInitializationWithDefaultValues() throws Exception { .build(); - final var client = s3ClientProvider.buildClientIfNeeded(S3ClientSettings.create(settings), repoSettings); + final var client = + s3ClientProvider.buildClientIfNeeded( + S3ClientSettings.create(settings).get("default"), + repoSettings + ).v2(); final var amazonS3Client = (AmazonS3Client) client; assertEquals(S3ClientProvider.HTTP_USER_AGENT, amazonS3Client.getClientConfiguration().getUserAgentPrefix()); @@ -123,11 +156,16 @@ void testMaxRetriesOverridesClientSettings() throws IOException { final var s3ClientProvider = new S3ClientProvider(); final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "http://endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "http://endpoint") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var settings = Settings.builder().setSecureSettings(secureSettings).build(); @@ -137,7 +175,11 @@ void testMaxRetriesOverridesClientSettings() throws IOException { Settings.builder() .put(CommonSettings.RepositorySettings.MAX_RETRIES.getKey(), 20) .build(); - final var client = s3ClientProvider.buildClientIfNeeded(S3ClientSettings.create(settings), repoSettings); + final var client = + s3ClientProvider.buildClientIfNeeded( + S3ClientSettings.create(settings).get("default"), + repoSettings + ).v2(); final var amazonS3Client = (AmazonS3Client) client; assertEquals(S3ClientProvider.HTTP_USER_AGENT, amazonS3Client.getClientConfiguration().getUserAgentPrefix()); @@ -157,11 +199,16 @@ void testEndpointOverridesClientSettings() throws Exception { final var s3ClientProvider = new S3ClientProvider(); final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "http://endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "http://endpoint") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var settings = Settings.builder().setSecureSettings(secureSettings).build(); @@ -172,7 +219,11 @@ void testEndpointOverridesClientSettings() throws Exception { .put(CommonSettings.RepositorySettings.MAX_RETRIES.getKey(), 20) .put(S3ClientProvider.ENDPOINT_NAME.getKey(), "http://new-endpoint") .build(); - final var client = s3ClientProvider.buildClientIfNeeded(S3ClientSettings.create(settings), repoSettings); + final var client = + s3ClientProvider.buildClientIfNeeded( + S3ClientSettings.create(settings).get("default"), + repoSettings + ).v2(); final var amazonS3Client = (AmazonS3Client) client; assertEquals(S3ClientProvider.HTTP_USER_AGENT, amazonS3Client.getClientConfiguration().getUserAgentPrefix()); diff --git a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettingsTest.java b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettingsTest.java index 6a5ae5a..a7caa0f 100644 --- a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettingsTest.java +++ b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3ClientSettingsTest.java @@ -28,6 +28,14 @@ import com.amazonaws.ClientConfiguration; import org.junit.jupiter.api.Test; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.AWS_ACCESS_KEY_ID; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.AWS_SECRET_ACCESS_KEY; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.ENDPOINT; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.MAX_RETRIES; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.PRIVATE_KEY_FILE; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.PUBLIC_KEY_FILE; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.READ_TIMEOUT; +import static io.aiven.elasticsearch.repositories.s3.S3ClientSettings.USE_THROTTLE_RETRIES; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -38,27 +46,35 @@ class S3ClientSettingsTest extends RsaKeyAwareTest { void failsForEmptyAwsEndpoint() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var noEndpointSettings = Settings.builder().setSecureSettings(secureSettings).build(); final var t = assertThrows(IllegalArgumentException.class, () -> S3ClientSettings.create(noEndpointSettings)); - assertEquals("Settings with name aiven.s3.client.endpoint hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.s3.default.client.endpoint hasn't been set", t.getMessage()); } @Test void failsForEmptyAwsAccessKeyID() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "ENDPOINT") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "ENDPOINT") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var noAwsAccessKeyId = Settings.builder().setSecureSettings(secureSettings).build(); @@ -66,7 +82,7 @@ void failsForEmptyAwsAccessKeyID() throws IOException { assertThrows(IllegalArgumentException.class, () -> S3ClientSettings.create(noAwsAccessKeyId)); assertEquals( - "Settings with name aiven.s3.client.aws_access_key_id hasn't been set", + "Settings with name aiven.s3.default.client.aws_access_key_id hasn't been set", t.getMessage()); } @@ -74,10 +90,13 @@ void failsForEmptyAwsAccessKeyID() throws IOException { void failsForEmptyAwsSecretAccessKey() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.ENDPOINT.getKey(), "ENDPOINT") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), "ENDPOINT") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var noAwsAccessKeyId = Settings.builder().setSecureSettings(secureSettings).build(); @@ -85,7 +104,7 @@ void failsForEmptyAwsSecretAccessKey() throws IOException { assertThrows(IllegalArgumentException.class, () -> S3ClientSettings.create(noAwsAccessKeyId)); assertEquals( - "Settings with name aiven.s3.client.aws_secret_access_key hasn't been set", + "Settings with name aiven.s3.default.client.aws_secret_access_key hasn't been set", t.getMessage()); } @@ -94,15 +113,19 @@ void failsForEmptyPublicKey() throws IOException { final var settingsBuilder = Settings.builder().put(S3ClientSettings.ENDPOINT.getKey(), "endpoint"); final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.ENDPOINT.getKey(), "ENDPOINT") - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "ENDPOINT") + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var t = assertThrows(IllegalArgumentException.class, () -> S3ClientSettings.create(settingsBuilder.setSecureSettings(secureSettings).build())); - assertEquals("Settings with name aiven.s3.public_key_file hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.s3.default.public_key_file hasn't been set", t.getMessage()); } @Test @@ -110,31 +133,40 @@ void failsForEmptyPrivateKey() throws IOException { final var settingsBuilder = Settings.builder().put(S3ClientSettings.ENDPOINT.getKey(), "endpoint"); final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.ENDPOINT.getKey(), "ENDPOINT") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)); + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "ENDPOINT") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)); final var t = assertThrows(IllegalArgumentException.class, () -> S3ClientSettings.create(settingsBuilder.setSecureSettings(secureSettings).build())); - assertEquals("Settings with name aiven.s3.private_key_file hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.s3.default.private_key_file hasn't been set", t.getMessage()); } @Test void loadDefaultSettings() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "endpoint") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var settings = Settings.builder().setSecureSettings(secureSettings).build(); - final var s3ClientSettings = S3ClientSettings.create(settings); + final var s3ClientSettings = S3ClientSettings.create(settings).get("default"); assertEquals(s3ClientSettings.awsCredentials().getAWSAccessKeyId(), "AWS_ACCESS_KEY_ID"); assertEquals(s3ClientSettings.awsCredentials().getAWSSecretKey(), "AWS_SECRET_ACCESS_KEY"); @@ -148,21 +180,27 @@ void loadDefaultSettings() throws IOException { void overrideDefaultSettings() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(S3ClientSettings.AWS_ACCESS_KEY_ID.getKey(), "AWS_ACCESS_KEY_ID") - .setString(S3ClientSettings.AWS_SECRET_ACCESS_KEY.getKey(), "AWS_SECRET_ACCESS_KEY") - .setString(S3ClientSettings.ENDPOINT.getKey(), "http://endpoint") - .setFile(S3ClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(S3ClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString(AWS_ACCESS_KEY_ID.getConcreteSettingForNamespace("default").getKey(), + "AWS_ACCESS_KEY_ID") + .setString(AWS_SECRET_ACCESS_KEY.getConcreteSettingForNamespace("default").getKey(), + "AWS_SECRET_ACCESS_KEY") + .setString(ENDPOINT.getConcreteSettingForNamespace("default").getKey(), + "http://endpoint") + .setFile(PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)) + .setFile(PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem)); final var settings = Settings.builder() - .put(S3ClientSettings.MAX_RETRIES.getKey(), 12) - .put(S3ClientSettings.READ_TIMEOUT.getKey(), TimeValue.timeValueMillis(1000L)) - .put(S3ClientSettings.USE_THROTTLE_RETRIES.getKey(), false) + .put(MAX_RETRIES.getConcreteSettingForNamespace("default").getKey(), 12) + .put(READ_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), + TimeValue.timeValueMillis(1000L)) + .put(USE_THROTTLE_RETRIES.getConcreteSettingForNamespace("default").getKey(), false) .setSecureSettings(secureSettings) .build(); - final var s3ClientSettings = S3ClientSettings.create(settings); + final var s3ClientSettings = S3ClientSettings.create(settings).get("default"); assertEquals(s3ClientSettings.awsCredentials().getAWSAccessKeyId(), "AWS_ACCESS_KEY_ID"); assertEquals(s3ClientSettings.awsCredentials().getAWSSecretKey(), "AWS_SECRET_ACCESS_KEY"); diff --git a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3StorageIOTest.java b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3StorageIOTest.java index 8d9131b..9ec2696 100644 --- a/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3StorageIOTest.java +++ b/repository-s3/src/test/java/io/aiven/elasticsearch/repositories/s3/S3StorageIOTest.java @@ -69,7 +69,7 @@ void deleteFilesUsingBulk() throws Exception { Files.newInputStream(privateKeyPem).readAllBytes()); final var s3StorageIO = - new S3RepositoryStorageIOProvider(null, encProvider) + new S3RepositoryStorageIOProvider(null) .createStorageIOFor( mockedAmazonS3, Settings.builder() @@ -120,7 +120,7 @@ void deleteDirectoriesUsingBulk() throws Exception { .thenReturn(mock(DeleteObjectsResult.class)); final var s3StorageIO = - new S3RepositoryStorageIOProvider(null, encProvider) + new S3RepositoryStorageIOProvider(null) .createStorageIOFor( mockedAmazonS3, Settings.builder() From 1c9619c6786c5e1c9d17d9188d910429a848f482 Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Sun, 13 Feb 2022 22:50:34 +0100 Subject: [PATCH 2/4] Migration GCS to affix settings All GCS settings have such prefix: aive.s3.*. - Pass Map of GcsClientSettings - EncryptionKeyProvider now creates/recreates together with GCS client --- .../repositories/gcs/GcsClientSettings.java | 182 ++++++++++++------ .../repositories/gcs/GcsRepositoryPlugin.java | 4 +- .../gcs/GcsRepositoryStorageIOProvider.java | 6 +- .../repositories/gcs/GcsSettingsProvider.java | 9 +- .../gcs/GcsClientProviderTest.java | 112 +++++++---- .../gcs/GcsClientSettingsTest.java | 49 ++++- 6 files changed, 244 insertions(+), 118 deletions(-) diff --git a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettings.java b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettings.java index 6fbcdb5..12139ca 100644 --- a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettings.java +++ b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettings.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.opensearch.common.settings.SecureSetting; @@ -30,51 +33,94 @@ import com.google.auth.oauth2.GoogleCredentials; import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.checkSettings; -import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.withPrefix; +import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.getConfigValue; +import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.readInputStream; public class GcsClientSettings implements CommonSettings.ClientSettings { - public static final Setting PUBLIC_KEY_FILE = - SecureSetting.secureFile(withPrefix("gcs.public_key_file"), null); - - public static final Setting PRIVATE_KEY_FILE = - SecureSetting.secureFile(withPrefix("gcs.private_key_file"), null); - - public static final Setting CREDENTIALS_FILE_SETTING = - SecureSetting.secureFile(withPrefix("gcs.client.credentials_file"), null); - - public static final Setting PROXY_HOST = - Setting.simpleString(withPrefix("gcs.client.proxy.host"), Setting.Property.NodeScope); - - public static final Setting PROXY_PORT = - SecureSetting.intSetting(withPrefix("gcs.client.proxy.port"), 0, 0, - Setting.Property.NodeScope); - - public static final Setting PROXY_USER_NAME = - SecureSetting.secureString(withPrefix("gcs.client.proxy.user_name"), null); - - public static final Setting PROXY_USER_PASSWORD = - SecureSetting.secureString(withPrefix("gcs.client.proxy.user_password"), null); - - public static final Setting PROJECT_ID = - Setting.simpleString(withPrefix("gcs.client.project_id"), Setting.Property.NodeScope); - - public static final Setting CONNECTION_TIMEOUT = - Setting.intSetting(withPrefix("gcs.client.connection_timeout"), -1, -1, - Setting.Property.NodeScope); - - public static final Setting READ_TIMEOUT = - Setting.intSetting(withPrefix("gcs.client.read_timeout"), -1, -1, - Setting.Property.NodeScope); + static final String GCS_PREFIX = AIVEN_PREFIX + "gcs."; + + public static final Setting.AffixSetting PUBLIC_KEY_FILE = + Setting.affixKeySetting( + GCS_PREFIX, + "public_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting PRIVATE_KEY_FILE = + Setting.affixKeySetting( + GCS_PREFIX, + "private_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting CREDENTIALS_FILE_SETTING = + Setting.affixKeySetting( + GCS_PREFIX, + "client.credentials_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting PROXY_HOST = + Setting.affixKeySetting( + GCS_PREFIX, + "client.proxy.host", + key -> Setting.simpleString(key, Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting PROXY_PORT = + Setting.affixKeySetting( + GCS_PREFIX, + "client.proxy.port", + key -> SecureSetting.intSetting(key, 0, 0, Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting PROXY_USER_NAME = + Setting.affixKeySetting( + GCS_PREFIX, + "client.proxy.user_name", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting PROXY_USER_PASSWORD = + Setting.affixKeySetting( + GCS_PREFIX, + "client.proxy.user_password", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting PROJECT_ID = + Setting.affixKeySetting( + GCS_PREFIX, + "client.project_id", + key -> Setting.simpleString(key, Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting CONNECTION_TIMEOUT = + Setting.affixKeySetting( + GCS_PREFIX, + "client.connection_timeout", + key -> Setting.intSetting(key, -1, -1, Setting.Property.NodeScope) + ); + + public static final Setting.AffixSetting READ_TIMEOUT = + Setting.affixKeySetting( + GCS_PREFIX, + "client.read_timeout", + key -> Setting.intSetting(key, -1, -1, Setting.Property.NodeScope) + ); /** The number of retries to use when an GCS request fails. */ - public static final Setting MAX_RETRIES_SETTING = - Setting.intSetting(withPrefix("gcs.client.max_retries"), 3, 0, - Setting.Property.NodeScope); + public static final Setting.AffixSetting MAX_RETRIES_SETTING = + Setting.affixKeySetting( + GCS_PREFIX, + "client.max_retries", + key -> Setting.intSetting(key, 3, 0, Setting.Property.NodeScope) + ); - private final InputStream publicKey; + private final byte[] publicKey; - private final InputStream privateKey; + private final byte[] privateKey; private final String projectId; @@ -95,8 +141,8 @@ public class GcsClientSettings implements CommonSettings.ClientSettings { private final int proxyPort; - private GcsClientSettings(final InputStream publicKey, - final InputStream privateKey, + private GcsClientSettings(final byte[] publicKey, + final byte[] privateKey, final String projectId, final GoogleCredentials gcsCredentials, final int connectionTimeout, @@ -119,41 +165,57 @@ private GcsClientSettings(final InputStream publicKey, this.proxyUserPassword = proxyUserPassword; } - public static GcsClientSettings create(final Settings settings) throws IOException { + public static Map create(final Settings settings) throws IOException { if (settings.isEmpty()) { throw new IllegalArgumentException("Settings for GC storage hasn't been set"); } - checkSettings(CREDENTIALS_FILE_SETTING, settings); - checkSettings(PUBLIC_KEY_FILE, settings); - checkSettings(PRIVATE_KEY_FILE, settings); - if (PROXY_PORT.exists(settings) && PROXY_PORT.get(settings) < 0) { + final Set clientNames = settings.getGroups(GCS_PREFIX).keySet(); + final var clientSettings = new HashMap(); + for (final var clientName : clientNames) { + clientSettings.put(clientName, createSettings(clientName, settings)); + } + return Map.copyOf(clientSettings); + } + + private static GcsClientSettings createSettings( + final String clientName, final Settings settings) throws IOException { + checkSettings(CREDENTIALS_FILE_SETTING, clientName, settings); + checkSettings(PUBLIC_KEY_FILE, clientName, settings); + checkSettings(PRIVATE_KEY_FILE, clientName, settings); + if (PROXY_PORT.getConcreteSettingForNamespace(clientName).exists(settings) + && PROXY_PORT.getConcreteSettingForNamespace(clientName).get(settings) < 0) { throw new IllegalArgumentException("Settings with name " + PROXY_PORT.getKey() + " must be greater than 0"); } return new GcsClientSettings( - PUBLIC_KEY_FILE.get(settings), - PRIVATE_KEY_FILE.get(settings), - PROJECT_ID.get(settings), - loadCredentials(settings), - CONNECTION_TIMEOUT.get(settings), - READ_TIMEOUT.get(settings), - MAX_RETRIES_SETTING.get(settings), - PROXY_HOST.get(settings), - PROXY_PORT.get(settings), - PROXY_USER_NAME.get(settings).toString(), - PROXY_USER_PASSWORD.get(settings).getChars()); + readInputStream(getConfigValue(settings, clientName, PUBLIC_KEY_FILE)), + readInputStream(getConfigValue(settings, clientName, PRIVATE_KEY_FILE)), + getConfigValue(settings, clientName, PROJECT_ID), + loadCredentials(settings, clientName), + getConfigValue(settings, clientName, CONNECTION_TIMEOUT), + getConfigValue(settings, clientName, READ_TIMEOUT), + getConfigValue(settings, clientName, MAX_RETRIES_SETTING), + getConfigValue(settings, clientName, PROXY_HOST), + getConfigValue(settings, clientName, PROXY_PORT), + getConfigValue(settings, clientName, PROXY_USER_NAME).toString(), + getConfigValue(settings, clientName, PROXY_USER_PASSWORD).getChars() + ); } - private static GoogleCredentials loadCredentials(final Settings settings) throws IOException { - try (final var in = CREDENTIALS_FILE_SETTING.get(settings)) { + private static GoogleCredentials loadCredentials( + final Settings settings, + final String clientName) throws IOException { + try (final var in = getConfigValue(settings, clientName, CREDENTIALS_FILE_SETTING)) { return GoogleCredentials.fromStream(in); } } - public InputStream publicKey() { + @Override + public byte[] publicKey() { return publicKey; } - public InputStream privateKey() { + @Override + public byte[] privateKey() { return privateKey; } diff --git a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryPlugin.java b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryPlugin.java index f0e9925..0530d2d 100644 --- a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryPlugin.java +++ b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryPlugin.java @@ -25,12 +25,14 @@ import com.google.cloud.storage.Storage; +import static io.aiven.elasticsearch.repositories.gcs.GcsClientSettings.GCS_PREFIX; + public class GcsRepositoryPlugin extends AbstractRepositoryPlugin { public static final String REPOSITORY_TYPE = "aiven-gcs"; public GcsRepositoryPlugin(final Settings settings) { - super(REPOSITORY_TYPE, settings, new GcsSettingsProvider()); + super(REPOSITORY_TYPE, GCS_PREFIX, settings, new GcsSettingsProvider()); } @Override diff --git a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryStorageIOProvider.java b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryStorageIOProvider.java index 3c7c17b..827cb4f 100644 --- a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryStorageIOProvider.java +++ b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsRepositoryStorageIOProvider.java @@ -37,7 +37,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; import io.aiven.elasticsearch.repositories.io.CryptoIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.google.cloud.BatchResult; import com.google.cloud.storage.Blob; @@ -65,9 +64,8 @@ public class GcsRepositoryStorageIOProvider private static final Logger LOGGER = LoggerFactory.getLogger(GcsRepositoryStorageIOProvider.class); - public GcsRepositoryStorageIOProvider(final GcsClientSettings storageSettings, - final EncryptionKeyProvider encryptionKeyProvider) { - super(new GcsClientProvider(), storageSettings, encryptionKeyProvider); + public GcsRepositoryStorageIOProvider(final Map storageSettings) { + super(new GcsClientProvider(), storageSettings); } @Override diff --git a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsSettingsProvider.java b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsSettingsProvider.java index 0ba5c5d..7621f89 100644 --- a/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsSettingsProvider.java +++ b/repository-gcs/src/main/java/io/aiven/elasticsearch/repositories/gcs/GcsSettingsProvider.java @@ -23,7 +23,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositorySettingsProvider; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.google.cloud.storage.Storage; @@ -32,14 +31,10 @@ public class GcsSettingsProvider extends RepositorySettingsProvider createRepositoryStorageIOProvider( final Settings settings) throws IOException { + return Permissions.doPrivileged(() -> { final var gcsClientSettings = GcsClientSettings.create(settings); - final var encryptionKeyProvider = - EncryptionKeyProvider.of( - gcsClientSettings.publicKey().readAllBytes(), - gcsClientSettings.privateKey().readAllBytes() - ); - return new GcsRepositoryStorageIOProvider(gcsClientSettings, encryptionKeyProvider); + return new GcsRepositoryStorageIOProvider(gcsClientSettings); }); } diff --git a/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientProviderTest.java b/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientProviderTest.java index ba68a4d..ab693f3 100644 --- a/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientProviderTest.java +++ b/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientProviderTest.java @@ -48,9 +48,9 @@ void providerInitialization() throws Exception { final var gcsClientProvider = new GcsClientProvider(); final var settings = Settings.builder() .put(CommonSettings.RepositorySettings.BASE_PATH.getKey(), "base_path/") - .put(GcsClientSettings.CONNECTION_TIMEOUT.getKey(), 1) - .put(GcsClientSettings.READ_TIMEOUT.getKey(), 2) - .put(GcsClientSettings.PROJECT_ID.getKey(), "some_project") + .put(GcsClientSettings.CONNECTION_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 1) + .put(GcsClientSettings.READ_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 2) + .put(GcsClientSettings.PROJECT_ID.getConcreteSettingForNamespace("default").getKey(), "some_project") .setSecureSettings(createFullSecureSettings()).build(); final var repoSettings = @@ -58,7 +58,11 @@ void providerInitialization() throws Exception { .put("some_settings_1", 20) .put("some_settings_2", 210) .build(); - final var client = gcsClientProvider.buildClientIfNeeded(GcsClientSettings.create(settings), repoSettings); + final var client = + gcsClientProvider.buildClientIfNeeded( + GcsClientSettings.create(settings).get("default"), + repoSettings + ).v2(); assertTrue(client.getOptions().getTransportOptions() instanceof HttpTransportOptions); final var httpTransportOptions = (HttpTransportOptions) client.getOptions().getTransportOptions(); @@ -76,11 +80,11 @@ void provideInitializationWithProxyConfigurationWithUsernameAndPassword() throws final var gcsClientProvider = new GcsClientProvider(); final var proxySettingsWithUsernameAndPassword = Settings.builder() .put(CommonSettings.RepositorySettings.BASE_PATH.getKey(), "base_path/") - .put(GcsClientSettings.CONNECTION_TIMEOUT.getKey(), 1) - .put(GcsClientSettings.READ_TIMEOUT.getKey(), 2) - .put(GcsClientSettings.PROXY_HOST.getKey(), "socks.test.io") - .put(GcsClientSettings.PROXY_PORT.getKey(), 1234) - .put(GcsClientSettings.PROJECT_ID.getKey(), "some_project") + .put(GcsClientSettings.CONNECTION_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 1) + .put(GcsClientSettings.READ_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 2) + .put(GcsClientSettings.PROXY_HOST.getConcreteSettingForNamespace("default").getKey(), "socks.test.io") + .put(GcsClientSettings.PROXY_PORT.getConcreteSettingForNamespace("default").getKey(), 1234) + .put(GcsClientSettings.PROJECT_ID.getConcreteSettingForNamespace("default").getKey(), "some_project") .setSecureSettings(createFullSecureSettingsWithProxyUsernameAndPassword()).build(); final var repoSettings = Settings.builder() @@ -89,7 +93,10 @@ void provideInitializationWithProxyConfigurationWithUsernameAndPassword() throws .build(); final var client = gcsClientProvider - .buildClientIfNeeded(GcsClientSettings.create(proxySettingsWithUsernameAndPassword), repoSettings); + .buildClientIfNeeded( + GcsClientSettings.create(proxySettingsWithUsernameAndPassword).get("default"), + repoSettings + ).v2(); assertTrue(client.getOptions().getTransportOptions() instanceof HttpTransportOptions); @@ -115,11 +122,11 @@ void provideInitializationWithProxyConfigurationWithoutUsernameAndPassword() thr final var gcsClientProvider = new GcsClientProvider(); final var proxySettingsWithoutUsernameAndPassword = Settings.builder() .put(CommonSettings.RepositorySettings.BASE_PATH.getKey(), "base_path/") - .put(GcsClientSettings.CONNECTION_TIMEOUT.getKey(), 1) - .put(GcsClientSettings.READ_TIMEOUT.getKey(), 2) - .put(GcsClientSettings.PROXY_HOST.getKey(), "socks5.test.io") - .put(GcsClientSettings.PROXY_PORT.getKey(), 12345) - .put(GcsClientSettings.PROJECT_ID.getKey(), "some_project") + .put(GcsClientSettings.CONNECTION_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 1) + .put(GcsClientSettings.READ_TIMEOUT.getConcreteSettingForNamespace("default").getKey(), 2) + .put(GcsClientSettings.PROXY_HOST.getConcreteSettingForNamespace("default").getKey(), "socks5.test.io") + .put(GcsClientSettings.PROXY_PORT.getConcreteSettingForNamespace("default").getKey(), 12345) + .put(GcsClientSettings.PROJECT_ID.getConcreteSettingForNamespace("default").getKey(), "some_project") .setSecureSettings(createFullSecureSettings()).build(); final var repoSettings = Settings.builder() @@ -127,7 +134,10 @@ void provideInitializationWithProxyConfigurationWithoutUsernameAndPassword() thr .put("some_settings_2", 210) .build(); final var client = gcsClientProvider - .buildClientIfNeeded(GcsClientSettings.create(proxySettingsWithoutUsernameAndPassword), repoSettings); + .buildClientIfNeeded( + GcsClientSettings.create(proxySettingsWithoutUsernameAndPassword).get("default"), + repoSettings + ).v2(); assertTrue(client.getOptions().getTransportOptions() instanceof HttpTransportOptions); @@ -160,7 +170,10 @@ void providerInitializationWithDefaultValues() throws Exception { .build(); final var client = gcsClientProvider - .buildClientIfNeeded(GcsClientSettings.create(settings), repoSettings); + .buildClientIfNeeded( + GcsClientSettings.create(settings).get("default"), + repoSettings + ).v2(); assertNotNull(client); assertTrue(client.getOptions().getTransportOptions() instanceof HttpTransportOptions); @@ -186,7 +199,10 @@ void testMaxRetriesOverridesClientSettings() throws IOException { .build(); final var client = gcsClientProvider - .buildClientIfNeeded(GcsClientSettings.create(settings), repoSettings); + .buildClientIfNeeded( + GcsClientSettings.create(settings).get("default"), + repoSettings + ).v2(); assertNotNull(client); assertTrue(client.getOptions().getTransportOptions() instanceof HttpTransportOptions); @@ -216,43 +232,67 @@ private Proxy extractProxy(final NetHttpTransport netHttpTransport) throws Excep private DummySecureSettings createFullSecureSettings() throws IOException { return new DummySecureSettings() .setFile( - GcsClientSettings.CREDENTIALS_FILE_SETTING.getKey(), - getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json")) - .setFile(GcsClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)) - .setFile(GcsClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)); + GcsClientSettings.CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace("default").getKey(), + getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json") + ).setFile( + GcsClientSettings.PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ).setFile( + GcsClientSettings.PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ); } private DummySecureSettings createFullSecureSettingsWithProxyUsernameAndPassword() throws IOException { return new DummySecureSettings() .setFile( - GcsClientSettings.CREDENTIALS_FILE_SETTING.getKey(), - getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json")) - .setFile(GcsClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)) - .setFile(GcsClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setString(GcsClientSettings.PROXY_USER_NAME.getKey(), "some_user_name") - .setString(GcsClientSettings.PROXY_USER_PASSWORD.getKey(), "some_user_password"); + GcsClientSettings.CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace("default").getKey(), + getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json") + ).setFile( + GcsClientSettings.PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ).setFile( + GcsClientSettings.PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ).setString( + GcsClientSettings.PROXY_USER_NAME.getConcreteSettingForNamespace("default").getKey(), + "some_user_name" + ).setString( + GcsClientSettings.PROXY_USER_PASSWORD.getConcreteSettingForNamespace("default").getKey(), + "some_user_password" + ); } private DummySecureSettings createNoGcsCredentialFileSettings() throws IOException { return new DummySecureSettings() - .setFile(GcsClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(GcsClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setFile( + GcsClientSettings.PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ).setFile( + GcsClientSettings.PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); } private DummySecureSettings createPublicRsaKeyOnlySecureSettings() throws IOException { return new DummySecureSettings() .setFile( - GcsClientSettings.CREDENTIALS_FILE_SETTING.getKey(), - getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json")) - .setFile(GcsClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)); + GcsClientSettings.CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace("default").getKey(), + getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json") + ).setFile( + GcsClientSettings.PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem)); } private DummySecureSettings createPrivateRsaKeyOnlySecureSettings() throws IOException { return new DummySecureSettings() .setFile( - GcsClientSettings.CREDENTIALS_FILE_SETTING.getKey(), - getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json")) - .setFile(GcsClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + GcsClientSettings.CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace("default").getKey(), + getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json") + ).setFile( + GcsClientSettings.PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); } GoogleCredentials loadCredentials() throws IOException { diff --git a/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettingsTest.java b/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettingsTest.java index 5f19c7c..e2006cd 100644 --- a/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettingsTest.java +++ b/repository-gcs/src/test/java/io/aiven/elasticsearch/repositories/gcs/GcsClientSettingsTest.java @@ -36,16 +36,37 @@ class GcsClientSettingsTest extends RsaKeyAwareTest { @Test void loadSettings() throws Exception { - final var settings = createSettings() + final var settings = createSettings("default") .setSecureSettings( createSecureSettings( + "default", getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json"), Files.newInputStream(publicKeyPem), Files.newInputStream(privateKeyPem) ) ).build(); - final var gcsClientSettings = GcsClientSettings.create(settings); + final var gcsClientSettings = GcsClientSettings.create(settings).get("default"); + assertEquals("some_project", gcsClientSettings.projectId()); + assertEquals(1, gcsClientSettings.connectionTimeout()); + assertEquals(2, gcsClientSettings.readTimeout()); + + assertNotNull(gcsClientSettings.gcsCredentials()); + } + + @Test + void loadNonDefaultClientSettings() throws IOException { + final var settings = createSettings("some_client") + .setSecureSettings( + createSecureSettings( + "some_client", + getClass().getClassLoader().getResourceAsStream("test_gcs_creds.json"), + Files.newInputStream(publicKeyPem), + Files.newInputStream(privateKeyPem) + ) + ).build(); + + final var gcsClientSettings = GcsClientSettings.create(settings).get("some_client"); assertEquals("some_project", gcsClientSettings.projectId()); assertEquals(1, gcsClientSettings.connectionTimeout()); assertEquals(2, gcsClientSettings.readTimeout()); @@ -65,20 +86,28 @@ void throwsIllegalArgumentExceptionForEmptyCredentials() { ); } - Settings.Builder createSettings() { + Settings.Builder createSettings(final String clientName) { return Settings.builder() - .put(GcsClientSettings.CONNECTION_TIMEOUT.getKey(), 1) - .put(GcsClientSettings.READ_TIMEOUT.getKey(), 2) - .put(GcsClientSettings.PROJECT_ID.getKey(), "some_project"); + .put(GcsClientSettings.CONNECTION_TIMEOUT.getConcreteSettingForNamespace(clientName).getKey(), 1) + .put(GcsClientSettings.READ_TIMEOUT.getConcreteSettingForNamespace(clientName).getKey(), 2) + .put(GcsClientSettings.PROJECT_ID.getConcreteSettingForNamespace(clientName).getKey(), "some_project"); } - SecureSettings createSecureSettings(final InputStream googleCredential, + SecureSettings createSecureSettings(final String clientName, + final InputStream googleCredential, final InputStream publicKey, final InputStream privateKey) throws IOException { return new DummySecureSettings() - .setFile(GcsClientSettings.CREDENTIALS_FILE_SETTING.getKey(), googleCredential) - .setFile(GcsClientSettings.PUBLIC_KEY_FILE.getKey(), publicKey) - .setFile(GcsClientSettings.PRIVATE_KEY_FILE.getKey(), privateKey); + .setFile( + GcsClientSettings.CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName).getKey(), + googleCredential + ).setFile( + GcsClientSettings.PUBLIC_KEY_FILE.getConcreteSettingForNamespace(clientName).getKey(), + publicKey + ).setFile( + GcsClientSettings.PRIVATE_KEY_FILE.getConcreteSettingForNamespace(clientName).getKey(), + privateKey + ); } From 61a3d5f2f63745affe650e6c7485c2f32aff8b37 Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Sun, 13 Feb 2022 22:51:08 +0100 Subject: [PATCH 3/4] Migration Azure to affix settings All Azure settings have such prefix: aive.s3.*. - Pass Map of AzureClientSettings - EncryptionKeyProvider now creates/recreates together with Azure client --- .../azure/AzureClientSettings.java | 140 ++++++++++++------ .../azure/AzureRepositoryPlugin.java | 4 +- .../AzureRepositoryStorageIOProvider.java | 6 +- .../azure/AzureSettingsProvider.java | 5 +- .../azure/AzureClientProviderTest.java | 29 +++- .../azure/AzureClientSettingsTest.java | 138 ++++++++++------- 6 files changed, 207 insertions(+), 115 deletions(-) diff --git a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettings.java b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettings.java index ea821a4..27c9b3a 100644 --- a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettings.java +++ b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettings.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import org.opensearch.common.settings.SecureSetting; import org.opensearch.common.settings.SecureString; @@ -28,46 +31,84 @@ import io.aiven.elasticsearch.repositories.CommonSettings; import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.checkSettings; +import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.getConfigValue; import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.readInputStream; -import static io.aiven.elasticsearch.repositories.CommonSettings.ClientSettings.withPrefix; public class AzureClientSettings implements CommonSettings.ClientSettings { + static final String AZURE_PREFIX = AIVEN_PREFIX + "azure."; + static final String AZURE_CONNECTION_STRING_TEMPLATE = "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s"; - static final Setting AZURE_HTTP_POOL_MIN_THREADS = - Setting.intSetting(withPrefix("azure.http.thread_pool.min"), - Runtime.getRuntime().availableProcessors() * 2 - 1, 1, Setting.Property.NodeScope); - - static final Setting AZURE_HTTP_POOL_MAX_THREADS = - Setting.intSetting(withPrefix("azure.http.thread_pool.max"), - Runtime.getRuntime().availableProcessors() * 2 - 1, 1, Setting.Property.NodeScope); - - static final Setting AZURE_HTTP_POOL_KEEP_ALIVE = - Setting.timeSetting(withPrefix("azure.http.thread_pool.keep_alive"), TimeValue.timeValueSeconds(30L), - Setting.Property.NodeScope); - - static final Setting AZURE_HTTP_POOL_WORKING_QUEUE_SIZE = - Setting.intSetting(withPrefix("azure.http.thread_pool.working_queue_size"), 1000, 10, - Setting.Property.NodeScope); - - - public static final Setting PUBLIC_KEY_FILE = - SecureSetting.secureFile(withPrefix("azure.public_key_file"), null); - - public static final Setting PRIVATE_KEY_FILE = - SecureSetting.secureFile(withPrefix("azure.private_key_file"), null); - - public static final Setting AZURE_ACCOUNT = - SecureSetting.secureString(withPrefix("azure.client.account"), null); - - public static final Setting AZURE_ACCOUNT_KEY = - SecureSetting.secureString(withPrefix("azure.client.account.key"), null); + static final Setting.AffixSetting AZURE_HTTP_POOL_MIN_THREADS = + Setting.affixKeySetting( + AZURE_PREFIX, + "http.thread_pool.min", + key -> Setting.intSetting(key, + Runtime.getRuntime().availableProcessors() * 2 - 1, 1, + Setting.Property.NodeScope) + ); + + static final Setting.AffixSetting AZURE_HTTP_POOL_MAX_THREADS = + Setting.affixKeySetting( + AZURE_PREFIX, + "http.thread_pool.max", + key -> Setting.intSetting(key, + Runtime.getRuntime().availableProcessors() * 2 - 1, 1, + Setting.Property.NodeScope) + ); + + static final Setting.AffixSetting AZURE_HTTP_POOL_KEEP_ALIVE = + Setting.affixKeySetting( + AZURE_PREFIX, + "http.thread_pool.keep_alive", + key -> Setting.timeSetting(key, TimeValue.timeValueSeconds(30L), Setting.Property.NodeScope) + ); + + static final Setting.AffixSetting AZURE_HTTP_POOL_WORKING_QUEUE_SIZE = + Setting.affixKeySetting( + AZURE_PREFIX, + "http.thread_pool.working_queue_size", + key -> Setting.intSetting(key, 1000, 10, Setting.Property.NodeScope) + ); + + + public static final Setting.AffixSetting PUBLIC_KEY_FILE = + Setting.affixKeySetting( + AZURE_PREFIX, + "public_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting PRIVATE_KEY_FILE = + Setting.affixKeySetting( + AZURE_PREFIX, + "private_key_file", + key -> SecureSetting.secureFile(key, null) + ); + + public static final Setting.AffixSetting AZURE_ACCOUNT = + Setting.affixKeySetting( + AZURE_PREFIX, + "client.account", + key -> SecureSetting.secureString(key, null) + ); + + public static final Setting.AffixSetting AZURE_ACCOUNT_KEY = + Setting.affixKeySetting( + AZURE_PREFIX, + "client.account.key", + key -> SecureSetting.secureString(key, null) + ); //default is 3 please take a look ExponentialBackoff azure class - public static final Setting MAX_RETRIES = - Setting.intSetting(withPrefix("max_retries"), 3, Setting.Property.NodeScope); + public static final Setting.AffixSetting MAX_RETRIES = + Setting.affixKeySetting( + AZURE_PREFIX, + "max_retries", + key -> Setting.intSetting(key, 3, Setting.Property.NodeScope) + ); private final byte[] publicKey; @@ -119,22 +160,31 @@ public int maxRetries() { return maxRetries; } - public static AzureClientSettings create(final Settings settings) throws IOException { - checkSettings(AZURE_ACCOUNT, settings); - checkSettings(AZURE_ACCOUNT_KEY, settings); - checkSettings(PUBLIC_KEY_FILE, settings); - checkSettings(PRIVATE_KEY_FILE, settings); + public static Map create(final Settings settings) throws IOException { + final Set clientNames = settings.getGroups(AZURE_PREFIX).keySet(); + final var clientSettings = new HashMap(); + for (final var clientName : clientNames) { + clientSettings.put(clientName, createSettings(clientName, settings)); + } + return Map.copyOf(clientSettings); + } + + static AzureClientSettings createSettings(final String clientName, final Settings settings) throws IOException { + checkSettings(AZURE_ACCOUNT, clientName, settings); + checkSettings(AZURE_ACCOUNT_KEY, clientName, settings); + checkSettings(PUBLIC_KEY_FILE, clientName, settings); + checkSettings(PRIVATE_KEY_FILE, clientName, settings); return new AzureClientSettings( - readInputStream(PUBLIC_KEY_FILE, settings), - readInputStream(PRIVATE_KEY_FILE, settings), - AZURE_ACCOUNT.get(settings).toString(), - AZURE_ACCOUNT_KEY.get(settings).toString(), - MAX_RETRIES.get(settings), + readInputStream(getConfigValue(settings, clientName, PUBLIC_KEY_FILE)), + readInputStream(getConfigValue(settings, clientName, PRIVATE_KEY_FILE)), + getConfigValue(settings, clientName, AZURE_ACCOUNT).toString(), + getConfigValue(settings, clientName, AZURE_ACCOUNT_KEY).toString(), + getConfigValue(settings, clientName, MAX_RETRIES), new HttpThreadPoolSettings( - AZURE_HTTP_POOL_MIN_THREADS.get(settings), - AZURE_HTTP_POOL_MAX_THREADS.get(settings), - AZURE_HTTP_POOL_KEEP_ALIVE.get(settings).getMillis(), - AZURE_HTTP_POOL_WORKING_QUEUE_SIZE.get(settings)) + getConfigValue(settings, clientName, AZURE_HTTP_POOL_MIN_THREADS), + getConfigValue(settings, clientName, AZURE_HTTP_POOL_MAX_THREADS), + getConfigValue(settings, clientName, AZURE_HTTP_POOL_KEEP_ALIVE).getMillis(), + getConfigValue(settings, clientName, AZURE_HTTP_POOL_WORKING_QUEUE_SIZE)) ); } diff --git a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryPlugin.java b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryPlugin.java index 6704895..0fc25a7 100644 --- a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryPlugin.java +++ b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryPlugin.java @@ -25,12 +25,14 @@ import com.azure.storage.blob.BlobServiceClient; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_PREFIX; + public class AzureRepositoryPlugin extends AbstractRepositoryPlugin { public static final String REPOSITORY_TYPE = "aiven-azure"; public AzureRepositoryPlugin(final Settings settings) { - super(REPOSITORY_TYPE, settings, new AzureSettingsProvider()); + super(REPOSITORY_TYPE, AZURE_PREFIX, settings, new AzureSettingsProvider()); } @Override diff --git a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryStorageIOProvider.java b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryStorageIOProvider.java index b650614..af9fc94 100644 --- a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryStorageIOProvider.java +++ b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureRepositoryStorageIOProvider.java @@ -37,7 +37,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; import io.aiven.elasticsearch.repositories.io.CryptoIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; @@ -50,9 +49,8 @@ public class AzureRepositoryStorageIOProvider static final Setting CONTAINER_NAME = Setting.simpleString("container_name"); - public AzureRepositoryStorageIOProvider(final AzureClientSettings clientSettings, - final EncryptionKeyProvider encryptionKeyProvider) { - super(new AzureClientProvider(), clientSettings, encryptionKeyProvider); + public AzureRepositoryStorageIOProvider(final Map clientSettings) { + super(new AzureClientProvider(), clientSettings); } @Override diff --git a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureSettingsProvider.java b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureSettingsProvider.java index d791c37..104aa93 100644 --- a/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureSettingsProvider.java +++ b/repository-azure/src/main/java/io/aiven/elasticsearch/repositories/azure/AzureSettingsProvider.java @@ -23,7 +23,6 @@ import io.aiven.elasticsearch.repositories.Permissions; import io.aiven.elasticsearch.repositories.RepositorySettingsProvider; import io.aiven.elasticsearch.repositories.RepositoryStorageIOProvider; -import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; import com.azure.storage.blob.BlobServiceClient; @@ -34,10 +33,8 @@ protected RepositoryStorageIOProvider cr final Settings settings) throws IOException { return Permissions.doPrivileged(() -> { final var azureClientSettings = AzureClientSettings.create(settings); - final var encryptionKeyProvider = - EncryptionKeyProvider.of(azureClientSettings.publicKey(), azureClientSettings.privateKey()); return Permissions.doPrivileged(() -> - new AzureRepositoryStorageIOProvider(azureClientSettings, encryptionKeyProvider)); + new AzureRepositoryStorageIOProvider(azureClientSettings)); }); } diff --git a/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientProviderTest.java b/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientProviderTest.java index 35ebe77..b1c4ed7 100644 --- a/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientProviderTest.java +++ b/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientProviderTest.java @@ -39,6 +39,10 @@ import org.mockito.junit.jupiter.MockitoExtension; import static io.aiven.elasticsearch.repositories.CommonSettings.RepositorySettings.MAX_RETRIES; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_ACCOUNT; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_ACCOUNT_KEY; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.PRIVATE_KEY_FILE; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.PUBLIC_KEY_FILE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -109,8 +113,9 @@ void testBuildClient() throws Exception { .put("some_settings_2", 210) .build(); + final var azureClientSettings = AzureClientSettings.create(settings); final var client = azureClientProvider - .buildClientIfNeeded(AzureClientSettings.create(settings), repoSettings); + .buildClientIfNeeded(azureClientSettings.get("default"), repoSettings).v2(); assertEquals("AZURE_ACCOUNT", client.getAccountName()); @@ -131,8 +136,9 @@ void testMaxRetriesOverridesClientSettings() throws Exception { .put("some_settings_2", 210) .build(); + final var azureClientSettings = AzureClientSettings.create(settings); final var client = azureClientProvider - .buildClientIfNeeded(AzureClientSettings.create(settings), repoSettings); + .buildClientIfNeeded(azureClientSettings.get("default"), repoSettings).v2(); assertEquals("AZURE_ACCOUNT", client.getAccountName()); @@ -145,13 +151,22 @@ void testMaxRetriesOverridesClientSettings() throws Exception { private Settings createSettings() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "AZURE_ACCOUNT") - .setString(AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), "AZURE_ACCOUNT_KEY") - .setFile(AzureClientSettings.PUBLIC_KEY_FILE.getKey(), Files.newInputStream(publicKeyPem)) - .setFile(AzureClientSettings.PRIVATE_KEY_FILE.getKey(), Files.newInputStream(privateKeyPem)); + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "AZURE_ACCOUNT" + ).setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "AZURE_ACCOUNT_KEY" + ).setFile( + PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ).setFile( + PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); return Settings.builder() - .put(AzureClientSettings.MAX_RETRIES.getKey(), 12) + .put(AzureClientSettings.MAX_RETRIES.getConcreteSettingForNamespace("default").getKey(), 12) .setSecureSettings(secureSettings) .build(); } diff --git a/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettingsTest.java b/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettingsTest.java index be08ecf..c933f44 100644 --- a/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettingsTest.java +++ b/repository-azure/src/test/java/io/aiven/elasticsearch/repositories/azure/AzureClientSettingsTest.java @@ -27,6 +27,14 @@ import org.junit.jupiter.api.Test; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_ACCOUNT; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_ACCOUNT_KEY; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_HTTP_POOL_KEEP_ALIVE; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_HTTP_POOL_MAX_THREADS; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.AZURE_HTTP_POOL_MIN_THREADS; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.MAX_RETRIES; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.PRIVATE_KEY_FILE; +import static io.aiven.elasticsearch.repositories.azure.AzureClientSettings.PUBLIC_KEY_FILE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -34,57 +42,62 @@ class AzureClientSettingsTest extends RsaKeyAwareTest { @Test void failsForEmptyPublicKey() throws IOException { - final var settings = - Settings.builder() - .setSecureSettings( - new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "some_account") - .setString(AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), "some_account_key") - .setFile( - AzureClientSettings.PRIVATE_KEY_FILE.getKey(), - Files.newInputStream(privateKeyPem) - ) - ).build(); + final var securitySettings = + new DummySecureSettings() + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "some_account" + ).setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "some_account_key") + .setFile( + PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); + final var settings = Settings.builder().setSecureSettings(securitySettings).build(); final var t = assertThrows( IllegalArgumentException.class, () -> AzureClientSettings.create(settings)); - assertEquals("Settings with name aiven.azure.public_key_file hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.azure.default.public_key_file hasn't been set", + t.getMessage()); } @Test void failsForEmptyPrivateKey() throws IOException { - final var settings = - Settings.builder() - .setSecureSettings( - new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "some_account") - .setString( - AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), - "some_account_key" - ) - .setFile( - AzureClientSettings.PUBLIC_KEY_FILE.getKey(), - Files.newInputStream(publicKeyPem) - ) - ).build(); + final var securitySettings = + new DummySecureSettings() + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "some_account" + ).setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "some_account_key" + ).setFile( + PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) + ); + final var settings = Settings.builder().setSecureSettings(securitySettings).build(); final var t = assertThrows( IllegalArgumentException.class, () -> AzureClientSettings.create(settings)); - assertEquals("Settings with name aiven.azure.private_key_file hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.azure.default.private_key_file hasn't been set", t.getMessage()); } @Test void failsForEmptyAzureAccount() { final var secureSettings = new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), "some_key"); + .setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "some_key" + ); final var t = assertThrows(IllegalArgumentException.class, () -> AzureClientSettings.create(Settings.builder() .setSecureSettings(secureSettings).build())); - assertEquals("Settings with name aiven.azure.client.account hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.azure.default.client.account hasn't been set", t.getMessage()); } @Test @@ -92,33 +105,41 @@ void failsForEmptyAzureAccountKey() { final var secureSettings = new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "some_account"); + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "some_account" + ); final var t = assertThrows(IllegalArgumentException.class, () -> AzureClientSettings.create(Settings.builder() .setSecureSettings(secureSettings).build())); - assertEquals("Settings with name aiven.azure.client.account.key hasn't been set", t.getMessage()); + assertEquals("Settings with name aiven.azure.default.client.account.key hasn't been set", + t.getMessage()); } @Test void loadDefaultSettings() throws IOException { final var secureSettings = new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "some_account") - .setString(AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), "some_account_key") + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "some_account" + ).setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "some_account_key") .setFile( - AzureClientSettings.PUBLIC_KEY_FILE.getKey(), + PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), Files.newInputStream(publicKeyPem) ) .setFile( - AzureClientSettings.PRIVATE_KEY_FILE.getKey(), + PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), Files.newInputStream(privateKeyPem) ); final var azureClientSettings = - AzureClientSettings.create(Settings.builder().setSecureSettings(secureSettings).build()); + AzureClientSettings.create(Settings.builder().setSecureSettings(secureSettings).build()).get("default"); assertEquals("some_account", azureClientSettings.azureAccount()); assertEquals("some_account_key", azureClientSettings.azureAccountKey()); @@ -138,32 +159,41 @@ void loadDefaultSettings() throws IOException { @Test void overrideDefaultSettings() throws IOException { - final var settingsBuilder = Settings.builder(); - settingsBuilder.setSecureSettings( + final var securitySettings = new DummySecureSettings() - .setString(AzureClientSettings.AZURE_ACCOUNT.getKey(), "some_account") - .setString(AzureClientSettings.AZURE_ACCOUNT_KEY.getKey(), "some_account_key") + .setString( + AZURE_ACCOUNT.getConcreteSettingForNamespace("default").getKey(), + "some_account" + ).setString( + AZURE_ACCOUNT_KEY.getConcreteSettingForNamespace("default").getKey(), + "some_account_key") .setFile( - AzureClientSettings.PUBLIC_KEY_FILE.getKey(), - Files.newInputStream(publicKeyPem) + PUBLIC_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(publicKeyPem) ) .setFile( - AzureClientSettings.PRIVATE_KEY_FILE.getKey(), - Files.newInputStream(privateKeyPem) - ) - ); - - settingsBuilder.put(AzureClientSettings.MAX_RETRIES.getKey(), 42); - - settingsBuilder.put(AzureClientSettings.AZURE_HTTP_POOL_MIN_THREADS.getKey(), 10); - settingsBuilder.put(AzureClientSettings.AZURE_HTTP_POOL_MAX_THREADS.getKey(), 32); + PRIVATE_KEY_FILE.getConcreteSettingForNamespace("default").getKey(), + Files.newInputStream(privateKeyPem) + ); + final var settingsBuilder = Settings.builder(); + settingsBuilder.setSecureSettings(securitySettings); settingsBuilder.put( - AzureClientSettings.AZURE_HTTP_POOL_KEEP_ALIVE.getKey(), - TimeValue.timeValueMillis(1000L)); + MAX_RETRIES.getConcreteSettingForNamespace("default").getKey(), + 42 + ).put( + AZURE_HTTP_POOL_MIN_THREADS.getConcreteSettingForNamespace("default").getKey(), + 10 + ).put( + AZURE_HTTP_POOL_MAX_THREADS.getConcreteSettingForNamespace("default").getKey(), + 32 + ).put( + AZURE_HTTP_POOL_KEEP_ALIVE.getConcreteSettingForNamespace("default").getKey(), + TimeValue.timeValueMillis(1000L) + ); final var azureClientSettings = - AzureClientSettings.create(settingsBuilder.build()); + AzureClientSettings.create(settingsBuilder.build()).get("default"); assertEquals("some_account", azureClientSettings.azureAccount()); assertEquals("some_account_key", azureClientSettings.azureAccountKey()); From 799747949c6718b4c1da1d400612ea6edb266060 Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Sun, 13 Feb 2022 22:51:41 +0100 Subject: [PATCH 4/4] Migration to affix settings - Load of client settings as Map - EncryptionKeyProvider now creates/recreates together with client --- .../AbstractRepositoryPlugin.java | 15 +++---- .../repositories/ClientProvider.java | 15 ++++++- .../repositories/CommonSettings.java | 41 +++++++++++++------ .../RepositoryStorageIOProvider.java | 24 ++++++----- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/AbstractRepositoryPlugin.java b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/AbstractRepositoryPlugin.java index af4158e..a7bc48b 100644 --- a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/AbstractRepositoryPlugin.java +++ b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/AbstractRepositoryPlugin.java @@ -20,12 +20,9 @@ import java.io.UncheckedIOException; import java.security.Security; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; @@ -46,9 +43,9 @@ public abstract class AbstractRepositoryPlugin repositorySettingsProvider; + private final String repositorySettingsPrefix; - private final Set pluginSettingKeys; + private final RepositorySettingsProvider repositorySettingsProvider; static { try { @@ -59,11 +56,12 @@ public abstract class AbstractRepositoryPlugin repositorySettingsProvider) { this.repositoryType = repositoryType; + this.repositorySettingsPrefix = repositorySettingsPrefix; this.repositorySettingsProvider = repositorySettingsProvider; - this.pluginSettingKeys = getSettings().stream().map(Setting::getKey).collect(Collectors.toSet()); reload(settings); } @@ -86,10 +84,9 @@ private org.opensearch.repositories.blobstore.BlobStoreRepository createReposito @Override public void reload(final Settings settings) { try { - final var pluginKeys = settings.filter(pluginSettingKeys::contains); - if (!pluginKeys.isEmpty()) { + if (!settings.getGroups(repositorySettingsPrefix).isEmpty()) { LOGGER.info("Reload settings for repository type: {}", repositoryType); - repositorySettingsProvider.reload(pluginKeys); + repositorySettingsProvider.reload(settings); } } catch (final IOException ioe) { throw new UncheckedIOException(ioe); diff --git a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/ClientProvider.java b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/ClientProvider.java index bfb6d90..3abd7f7 100644 --- a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/ClientProvider.java +++ b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/ClientProvider.java @@ -20,8 +20,11 @@ import java.io.IOException; import java.util.Objects; +import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; +import io.aiven.elasticsearch.repositories.security.EncryptionKeyProvider; + public abstract class ClientProvider implements Closeable { private final Object lock = new Object(); @@ -30,18 +33,26 @@ public abstract class ClientProvider protected volatile C client; - public C buildClientIfNeeded(final S clientSettings, final Settings repositorySettings) throws IOException { + private volatile EncryptionKeyProvider encryptionKeyProvider; + + public Tuple buildClientIfNeeded( + final S clientSettings, + final Settings repositorySettings) throws IOException { synchronized (lock) { if (Objects.isNull(client)) { client = buildClient(clientSettings, repositorySettings); + encryptionKeyProvider = + EncryptionKeyProvider.of(clientSettings.publicKey(), clientSettings.privateKey()); previousRepositorySettings = repositorySettings; } else if (!previousRepositorySettings.equals(repositorySettings)) { closeClient(); + encryptionKeyProvider = + EncryptionKeyProvider.of(clientSettings.publicKey(), clientSettings.privateKey()); client = buildClient(clientSettings, repositorySettings); previousRepositorySettings = repositorySettings; } } - return client; + return Tuple.tuple(encryptionKeyProvider, client); } @Override diff --git a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/CommonSettings.java b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/CommonSettings.java index 09845f9..5382fba 100644 --- a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/CommonSettings.java +++ b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/CommonSettings.java @@ -29,29 +29,46 @@ public interface CommonSettings { interface ClientSettings { - String AIVEN_PREFIX = "aiven"; - - static String withPrefix(final String key) { - return String.format("%s.%s", AIVEN_PREFIX, key); - } - - static void checkSettings(final Setting setting, Settings keystoreSettings) { - if (!setting.exists(keystoreSettings)) { - throw new IllegalArgumentException("Settings with name " + setting.getKey() + " hasn't been set"); + String AIVEN_PREFIX = "aiven."; + + static void checkSettings( + final Setting.AffixSetting setting, + String clientName, + Settings keystoreSettings) { + if (!setting.getConcreteSettingForNamespace(clientName).exists(keystoreSettings)) { + throw new IllegalArgumentException("Settings with name " + + setting.getConcreteSettingForNamespace(clientName).getKey() + + " hasn't been set"); } } - static byte[] readInputStream(final Setting keySetting, - final Settings keystoreSettings) throws IOException { - try (final var in = keySetting.get(keystoreSettings)) { + static byte[] readInputStream(InputStream keyIn) throws IOException { + try (final var in = keyIn) { return in.readAllBytes(); } } + static T getConfigValue(Settings settings, String clientName, Setting.AffixSetting clientSetting) { + final Setting concreteSetting = clientSetting.getConcreteSettingForNamespace(clientName); + return concreteSetting.get(settings); + } + + byte[] publicKey(); + + byte[] privateKey(); + } interface RepositorySettings { + Setting CLIENT_NAME = + Setting.simpleString( + "client", + "default", + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); + Setting BASE_PATH = Setting.simpleString( "base_path", diff --git a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/RepositoryStorageIOProvider.java b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/RepositoryStorageIOProvider.java index a5e2185..dd6fdb6 100644 --- a/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/RepositoryStorageIOProvider.java +++ b/repository-commons/src/main/java/io/aiven/elasticsearch/repositories/RepositoryStorageIOProvider.java @@ -47,34 +47,36 @@ public abstract class RepositoryStorageIOProvider clientSettings; private SecretKey encryptionKey; - private final EncryptionKeyProvider encryptionKeyProvider; - private final ClientProvider clientProvider; - public RepositoryStorageIOProvider(final ClientProvider clientProvider, - final S clientSettings, - final EncryptionKeyProvider encryptionKeyProvider) { + public RepositoryStorageIOProvider(final ClientProvider clientProvider, final Map clientSettings) { this.clientProvider = clientProvider; this.clientSettings = clientSettings; - this.encryptionKeyProvider = encryptionKeyProvider; } public StorageIO createStorageIO(final String basePath, final Settings repositorySettings) throws IOException { final var bufferSize = Math.toIntExact(BUFFER_SIZE_SETTING.get(repositorySettings).getBytes()); - final var client = + final var encProviderAndClient = Permissions.doPrivileged(() -> { - final var c = clientProvider.buildClientIfNeeded(clientSettings, repositorySettings); - createOrRestoreEncryptionKey(c, basePath, repositorySettings); + final String clientName = CLIENT_NAME.get(repositorySettings); + final var c = + clientProvider.buildClientIfNeeded(clientSettings.get(clientName), repositorySettings); + createOrRestoreEncryptionKey(c.v2(), c.v1(), basePath, repositorySettings); return c; }); - return createStorageIOFor(client, repositorySettings, new CryptoIOProvider(encryptionKey, bufferSize)); + return createStorageIOFor( + encProviderAndClient.v2(), + repositorySettings, + new CryptoIOProvider(encryptionKey, bufferSize) + ); } private void createOrRestoreEncryptionKey(final C client, + final EncryptionKeyProvider encryptionKeyProvider, final String basePath, final Settings repositorySettings) throws IOException { if (Objects.isNull(encryptionKey)) {