From 5aa86fb0190def7131fde5c840ce866f919bdd18 Mon Sep 17 00:00:00 2001 From: Sanketh Nalli Date: Fri, 1 Dec 2023 09:43:00 -0800 Subject: [PATCH] [vcr-2.0] Create sync secret cred client (#2662) This patch adds code to create a synchronous secret-credential client for contacting Azure. --- .../azure/AzureCloudDestinationSync.java | 18 +++++++++++++++ .../github/ambry/cloud/azure/AzureUtils.java | 22 +++++++++++++------ .../ClientSecretCredentialStorageClient.java | 19 ++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureCloudDestinationSync.java b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureCloudDestinationSync.java index b5ad1cb49a..3edbc15045 100644 --- a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureCloudDestinationSync.java +++ b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureCloudDestinationSync.java @@ -116,6 +116,24 @@ public AzureCloudDestinationSync(VerifiableProperties verifiableProperties, Metr * cloudConfig.cloudRecentBlobCacheLimit = 0; unnecessary, as repl-logic avoids duplicate messages any ways * cloudConfig.vcrMinTtlDays = Infinite; Just upload each blob, don't complicate it. * + * Client configs + * ============== + * azureStorageClientClass = com.github.ambry.cloud.azure.ConnectionStringBasedStorageClient + * azureStorageConnectionString = + * + * OR, + * + * azureStorageClientClass = com.github.ambry.cloud.azure.ClientSecretCredentialStorageClient + * azureIdentityTenantId = + * azureIdentityClientId = + * azureIdentitySecret = + * azureIdentityProxyHost = null + * azureIdentityProxyPort = null + * + * azureStorageEndpoint = https://.blob.core.windows.net + * vcrProxyHost = null + * vcrProxyPort = null + * * Compaction Configs * ================== * diff --git a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureUtils.java b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureUtils.java index 09d820df86..3724afe2ef 100644 --- a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureUtils.java +++ b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureUtils.java @@ -34,12 +34,17 @@ class AzureUtils { * @param azureCloudConfig the configs. */ static void validateAzureIdentityConfigs(AzureCloudConfig azureCloudConfig) { - if (azureCloudConfig.azureIdentityTenantId.isEmpty() || azureCloudConfig.azureIdentityClientId.isEmpty() - || azureCloudConfig.azureIdentitySecret.isEmpty()) { + if (azureCloudConfig.azureIdentityTenantId == null || azureCloudConfig.azureIdentityTenantId.isEmpty()) { throw new IllegalArgumentException( - String.format("One of the required configs for using ClientSecretCredential (%s, %s, %s) is missing", - AzureCloudConfig.AZURE_IDENTITY_TENANT_ID, AzureCloudConfig.AZURE_IDENTITY_CLIENT_ID, - AzureCloudConfig.AZURE_IDENTITY_SECRET)); + String.format("%s is null or empty", AzureCloudConfig.AZURE_IDENTITY_TENANT_ID)); + } + if (azureCloudConfig.azureIdentityClientId == null || azureCloudConfig.azureIdentityClientId.isEmpty()) { + throw new IllegalArgumentException( + String.format("%s is null or empty", AzureCloudConfig.AZURE_IDENTITY_CLIENT_ID)); + } + if (azureCloudConfig.azureIdentitySecret == null || azureCloudConfig.azureIdentitySecret.isEmpty()) { + throw new IllegalArgumentException( + String.format("%s is null or empty", AzureCloudConfig.AZURE_IDENTITY_SECRET)); } } @@ -49,10 +54,13 @@ static void validateAzureIdentityConfigs(AzureCloudConfig azureCloudConfig) { */ static ClientSecretCredential getClientSecretCredential(AzureCloudConfig azureCloudConfig) { ClientSecretCredentialBuilder builder = - new ClientSecretCredentialBuilder().tenantId(azureCloudConfig.azureIdentityTenantId) + new ClientSecretCredentialBuilder() + .tenantId(azureCloudConfig.azureIdentityTenantId) .clientId(azureCloudConfig.azureIdentityClientId) .clientSecret(azureCloudConfig.azureIdentitySecret); - if (!azureCloudConfig.azureIdentityProxyHost.isEmpty()) { + if (azureCloudConfig.azureIdentityProxyHost == null || azureCloudConfig.azureIdentityProxyHost.isEmpty()) { + logger.info("Not using proxy for ClientSecretCredential as it is null or an empty string"); + } else { logger.info("Using proxy for ClientSecretCredential: {}:{}", azureCloudConfig.azureIdentityProxyHost, azureCloudConfig.azureIdentityProxyPort); ProxyOptions proxyOptions = new ProxyOptions(ProxyOptions.Type.HTTP, diff --git a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/ClientSecretCredentialStorageClient.java b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/ClientSecretCredentialStorageClient.java index ef224acdb4..9ba7120d1e 100644 --- a/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/ClientSecretCredentialStorageClient.java +++ b/ambry-cloud/src/main/java/com/github/ambry/cloud/azure/ClientSecretCredentialStorageClient.java @@ -88,4 +88,23 @@ protected boolean handleExceptionAndHintRetry(BlobStorageException blobStorageEx // no need to request a retry on 403 since the credential impl handles token refresh internally. return false; } + + /** + * Creates a synchronous client that uses a secret and a token to authenticate and contact Azure Blob Storage. + * @param httpClient {@link HttpClient} object. + * @param configuration {@link Configuration} object. + * @param retryOptions {@link RequestRetryOptions} object. + * @param azureCloudConfig {@link AzureCloudConfig} object. + * @return + */ + protected BlobServiceClient buildBlobServiceSyncClient(HttpClient httpClient, Configuration configuration, + RequestRetryOptions retryOptions, AzureCloudConfig azureCloudConfig) { + return new BlobServiceClientBuilder() + .credential(AzureUtils.getClientSecretCredential(azureCloudConfig)) + .endpoint(azureCloudConfig.azureStorageEndpoint) + .httpClient(httpClient) + .retryOptions(retryOptions) + .configuration(configuration) + .buildClient(); + } }