Skip to content

Add support for AzureDefaultCredential-based auth #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions azure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@
<artifactId>jgroups</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
Expand Down
90 changes: 49 additions & 41 deletions azure/src/main/java/org/jgroups/protocols/azure/AZURE_PING.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.time.Duration;
import java.util.List;

import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.StorageCredentials;
import com.microsoft.azure.storage.StorageCredentialsAccountAndKey;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.microsoft.azure.storage.blob.ListBlobItem;
import com.azure.identity.DefaultAzureCredentialBuilder;

import com.azure.storage.blob.*;
import com.azure.storage.blob.models.BlobItem;
import com.azure.storage.blob.models.ListBlobsOptions;
import com.azure.storage.common.StorageSharedKeyCredential;
import org.jgroups.Address;
import org.jgroups.annotations.Property;
import org.jgroups.conf.ClassConfigurator;
Expand All @@ -49,7 +48,7 @@ public class AZURE_PING extends FILE_PING {
@Property(description = "The name of the storage account.")
protected String storage_account_name;

@Property(description = "The secret account access key.", exposeAsManagedAttribute = false)
@Property(description = "The secret account access key, can be left blank to try to obtain credentials from the environment.", exposeAsManagedAttribute = false)
protected String storage_access_key;

@Property(description = "Container to store ping information in. Must be valid DNS name.")
Expand All @@ -63,7 +62,9 @@ public class AZURE_PING extends FILE_PING {

public static final int STREAM_BUFFER_SIZE = 4096;

private CloudBlobContainer containerReference;
private BlobContainerClient containerClient;

private static final Duration TIMEOUT = Duration.ofSeconds(1);

static {
ClassConfigurator.addProtocol((short) 530, AZURE_PING.class);
Expand All @@ -77,18 +78,25 @@ public void init() throws Exception {
// Can throw IAEs
this.validateConfiguration();

final BlobServiceClientBuilder blobClientBuilder = new BlobServiceClientBuilder();
if (storage_access_key != null) {
blobClientBuilder.credential(new StorageSharedKeyCredential(storage_account_name, storage_access_key));
} else {
blobClientBuilder.credential(new DefaultAzureCredentialBuilder().build());
}

blobClientBuilder.endpoint(new BlobUrlParts()
.setAccountName(storage_account_name)
.setScheme(use_https ? "https" : "http")
.setHost(endpoint_suffix) // endpoint suffix = host base component
.toUrl().toString());


try {
StorageCredentials credentials = new StorageCredentialsAccountAndKey(storage_account_name, storage_access_key);
CloudStorageAccount storageAccount;
if (endpoint_suffix != null) {
storageAccount = new CloudStorageAccount(credentials, use_https, endpoint_suffix);
} else {
storageAccount = new CloudStorageAccount(credentials, use_https);
}
CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
containerReference = blobClient.getContainerReference(container);
boolean created = containerReference.createIfNotExists();
BlobServiceClient blobClient = blobClientBuilder.buildClient();
containerClient = blobClient.getBlobContainerClient(container);

boolean created = containerClient.createIfNotExists();
if (created) {
log.info("Created container named '%s'.", container);
} else {
Expand All @@ -107,11 +115,11 @@ public void validateConfiguration() throws IllegalArgumentException {
|| container.startsWith("-") || container.length() < 3 || container.length() > 63) {
throw new IllegalArgumentException("Container name must be configured and must meet Azure requirements (must be a valid DNS name).");
}
// Account name and access key must be both configured for write access
if (storage_account_name == null || storage_access_key == null) {
throw new IllegalArgumentException("Account name and key must be configured.");
// Account name and must be configured
if (storage_account_name == null) {
throw new IllegalArgumentException("Account name must be configured.");
}
// Lets inform users here that https would be preferred
// Let's inform users here that https would be preferred
if (!use_https) {
log.info("Configuration is using HTTP, consider switching to HTTPS instead.");
}
Expand All @@ -132,15 +140,14 @@ protected void readAll(final List<Address> members, final String clustername, fi

String prefix = sanitize(clustername);

Iterable<ListBlobItem> listBlobItems = containerReference.listBlobs(prefix);
for (ListBlobItem blobItem : listBlobItems) {
Iterable<BlobItem> blobItems = containerClient.listBlobs(new ListBlobsOptions().setPrefix(prefix), TIMEOUT);
for (BlobItem blobItem : blobItems) {
try {
// If the item is a blob and not a virtual directory.
// n.b. what an ugly API this is
if (blobItem instanceof CloudBlob) {
CloudBlob blob = (CloudBlob) blobItem;
if (!blobItem.isPrefix()) {
BlobClient blobClient = containerClient.getBlobClient(blobItem.getName());
ByteArrayOutputStream os = new ByteArrayOutputStream(STREAM_BUFFER_SIZE);
blob.download(os);
blobClient.downloadStream(os);
byte[] pingBytes = os.toByteArray();
parsePingData(pingBytes, members, responses);
}
Expand Down Expand Up @@ -189,8 +196,8 @@ protected void write(final List<PingData> list, final String clustername) {
byte[] data = out.toByteArray();

// Upload the file
CloudBlockBlob blob = containerReference.getBlockBlobReference(filename);
blob.upload(new ByteArrayInputStream(data), data.length);
BlobClient blobClient = containerClient.getBlobClient(filename);
blobClient.upload(new ByteArrayInputStream(data), data.length);

} catch (Exception ex) {
log.error("Error marshalling and uploading ping data.", ex);
Expand All @@ -207,8 +214,8 @@ protected void remove(final String clustername, final Address addr) {
String filename = addressToFilename(clustername, addr);

try {
CloudBlockBlob blob = containerReference.getBlockBlobReference(filename);
boolean deleted = blob.deleteIfExists();
BlobClient blobClient = containerClient.getBlobClient(filename);
boolean deleted = blobClient.deleteIfExists();

if (deleted) {
log.debug("Tried to delete file '%s' but it was already deleted.", filename);
Expand All @@ -229,17 +236,18 @@ protected void removeAll(String clustername) {

clustername = sanitize(clustername);

Iterable<ListBlobItem> listBlobItems = containerReference.listBlobs(clustername);
for (ListBlobItem blobItem : listBlobItems) {
Iterable<BlobItem> blobItems = containerClient.listBlobs(
new ListBlobsOptions().setPrefix(clustername), TIMEOUT);
for (BlobItem blobItem : blobItems) {
try {
// If the item is a blob and not a virtual directory.
if (blobItem instanceof CloudBlob) {
CloudBlob blob = (CloudBlob) blobItem;
boolean deleted = blob.deleteIfExists();
if (!blobItem.isPrefix()) {
BlobClient blobClient = containerClient.getBlobClient(blobItem.getName());
boolean deleted = blobClient.deleteIfExists();
if (deleted) {
log.trace("Deleted file '%s'.", blob.getName());
log.trace("Deleted file '%s'.", blobItem.getName());
} else {
log.debug("Tried to delete file '%s' but it was already deleted.", blob.getName());
log.debug("Tried to delete file '%s' but it was already deleted.", blobItem.getName());
}
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,15 @@ public class AZURE_PINGDiscoveryTest {
public void testProtocolStack() throws Exception {
JChannel channel = new JChannel(STACK_XML_CONFIGURATION);

channel.getProtocolStack().getProtocols().replaceAll(protocol -> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an automatic conversion that my IDE performed to use "try-with-resources".

if (protocol instanceof AZURE_PING) {
return new AZURE_PING();
} else {
return protocol;
}
});

try {
try (channel) {
channel.getProtocolStack().getProtocols().replaceAll(protocol -> {
if (protocol instanceof AZURE_PING) {
return new AZURE_PING();
} else {
return protocol;
}
});
channel.connect(RANDOM_CLUSTER_NAME);
} finally {
channel.close();
}

}
Expand Down
19 changes: 9 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

<properties>
<version.org.jgroups>5.3.6.Final</version.org.jgroups>
<version.com.microsoft.azure.azure-storage>8.6.6</version.com.microsoft.azure.azure-storage>
<version.com.azure-azure-sdk-bom>1.2.23</version.com.azure-azure-sdk-bom>

<!-- Overridden versions as managed by WildFly -->
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not check if the updated Azure SDK components still cause issues here or (worse) in other parts of the dependency tree....

<version.com.fasterxml.jackson>2.17.1</version.com.fasterxml.jackson> <!-- azure-storage uses 2.6.0 -->
Expand All @@ -81,18 +81,17 @@
<artifactId>jgroups</artifactId>
<version>${version.org.jgroups}</version>
</dependency>

<!-- Pull in Azure bills of material to ensure that the SDK components fit together -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>${version.com.microsoft.azure.azure-storage}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<groupId>com.azure</groupId>
<artifactId>azure-sdk-bom</artifactId>
<version>${version.com.azure-azure-sdk-bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>


<!-- Override azure-storage dependencies versions with WildFly-managed ones -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
Expand Down