Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public AgroalVaultCredentialsProviderPassword(String credentialsProviderName, Cr
@Override
public Properties asProperties() {
Properties properties = new Properties();
Map<String, String> credentials = credentialsProvider.getCredentials(getWord());
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(getWord()).await().indefinitely();
credentials.forEach((key, value) -> properties.setProperty(key, value));
return properties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

import java.util.Map;

import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.infrastructure.Infrastructure;

/**
* Provides an indirection between credentials consumers such as Agroal and implementers such as Vault.
* <p>
* Quarkus extensions <strong>MUST</strong> invoke the asynchronous variant. that is {@link #getCredentialsAsync(String)}.
* <p>
* The default implementation of asynchronous variant invokes the synchronous {@link #getCredentials(String)} on a worker
* thread.
*/
public interface CredentialsProvider {

Expand All @@ -12,11 +20,23 @@ public interface CredentialsProvider {
String EXPIRATION_TIMESTAMP_PROPERTY_NAME = "expires-at";

/**
* Returns the credentials for a given credentials provider
* Returns the credentials for a given credentials provider.
*
* @param credentialsProviderName the name of the credentials provider, which can be used to retrieve custom configuration
* @return the credentials
*/
Map<String, String> getCredentials(String credentialsProviderName);
default Map<String, String> getCredentials(String credentialsProviderName) {
throw new UnsupportedOperationException("Either `getCredentials` or `getCredentialsAsync` must be implemented`");
}

/**
* Returns the credentials for a given credentials provider.
*
* @param credentialsProviderName the name of the credentials provider, which can be used to retrieve custom configuration
* @return a {@link Uni} completed with the credentials, or failed
*/
default Uni<Map<String, String>> getCredentialsAsync(String credentialsProviderName) {
return Uni.createFrom().item(() -> getCredentials(credentialsProviderName))
.runSubscriptionOn(Infrastructure.getDefaultExecutor());
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure it is correct, this is not where the subscription should be enabled, as the consumer of the api may be using another pool, this is probably what is causing the test failures...

I believe the default implementation should be return Uni.createFrom().item(getCredentials(credentialsProviderName));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what you mean with subscription enabled.

Subscription does not happen here. runSubscriptionOn operator configures the Uni so that, when the subscription happens, it happens on the default executor (a Quarkus worker thread).

Since existing getCrendentials implementation are synchronous, it is safer to invoke the method on a worker thread.

Copy link
Member

Choose a reason for hiding this comment

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

@tsegismont Sorry, I meant to say, that this is not a concern of this api method. It is a problem for the consumers of this api.

I'm 100% sure tests will pass without it. Otherwise it becomes a breaking change unfortunately.

Copy link
Member

Choose a reason for hiding this comment

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

To avoid some embarassment, let me say I'm quite sure it will be fixed without it, rather than quoting 100% 🙂.
I support this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem was not with the default implementation of getCredentialsAsync, but with the synchronous nature of io.quarkus.oidc.common.runtime.OidcCommonUtils#fromCredentialsProvider even though this method is invoked on an event loop thread.

I reverted the change in this method and added a comment. Ideally, the problem would be addressed in a follow-up PR, but I'm not knowledgeable with this area. I could help with a PR review though

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @tsegismont.
When I get to that, I'll definitely cc you.
IMHO though, it would still be better to avoid setting a particular thread pool in the default implementation, since its role is to avoid breaking existing code, not to offer an actual async implementation.
Custom extensions or code that has no async CP requirements or immediate capabilites may have no other option but to keep the deprecated method ref.
IMHO your original update to OidcCommonUtils should've worked as you typed it.

In any case, thanks for the PR

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ private UsernamePassword determineUserNamePassword(CredentialConfig config) {
String beanName = config.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = config.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
return new UsernamePassword(user, password.toCharArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ public String get() {
String providerName = provider.name().orElse(null);
String keyringName = provider.keyringName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(providerName);
// getCredentials invocation may block the event loop
return credentialsProvider.getCredentials(keyringName).get(provider.key().get());
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,45 @@
import static io.quarkus.credentials.CredentialsProvider.USER_PROPERTY_NAME;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

import io.quarkus.credentials.CredentialsProvider;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.UniHelper;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.sqlclient.SqlConnectOptions;

public class ConnectOptionsSupplier<CO extends SqlConnectOptions> implements Supplier<Future<CO>> {

private final Vertx vertx;
private final CredentialsProvider credentialsProvider;
private final String credentialsProviderName;
private final List<CO> connectOptionsList;
private final UnaryOperator<CO> connectOptionsCopy;
private final Callable<CO> blockingCodeHandler;
private final AtomicInteger idx = new AtomicInteger();

public ConnectOptionsSupplier(Vertx vertx, CredentialsProvider credentialsProvider, String credentialsProviderName,
public ConnectOptionsSupplier(CredentialsProvider credentialsProvider, String credentialsProviderName,
List<CO> connectOptionsList, UnaryOperator<CO> connectOptionsCopy) {
this.vertx = vertx;
this.credentialsProvider = credentialsProvider;
this.credentialsProviderName = credentialsProviderName;
this.connectOptionsList = connectOptionsList;
this.connectOptionsCopy = connectOptionsCopy;
this.blockingCodeHandler = new BlockingCodeHandler();
}

@Override
public Future<CO> get() {
return vertx.executeBlocking(blockingCodeHandler, false);
}

private class BlockingCodeHandler implements Callable<CO>, IntUnaryOperator {

final AtomicInteger idx = new AtomicInteger();

@Override
public CO call() {
Map<String, String> credentials = credentialsProvider.getCredentials(credentialsProviderName);
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);

int nextIdx = idx.getAndUpdate(this);

CO connectOptions = connectOptionsCopy.apply(connectOptionsList.get(nextIdx));
connectOptions.setUser(user).setPassword(password);

return connectOptions;
}

@Override
public int applyAsInt(int previousIdx) {
return previousIdx == connectOptionsList.size() - 1 ? 0 : previousIdx + 1;
}
int nextIdx = idx.getAndUpdate(previousIdx -> previousIdx == connectOptionsList.size() - 1 ? 0 : previousIdx + 1);
CO connectOptions = connectOptionsCopy.apply(connectOptionsList.get(nextIdx));
return Uni.combine()
.all()
.unis(credentialsProvider.getCredentialsAsync(credentialsProviderName), Uni.createFrom().item(connectOptions))
.with((credentials, co) -> {
co.setUser(credentials.get(USER_PROPERTY_NAME));
co.setPassword(credentials.get(PASSWORD_PROPERTY_NAME));
return co;
})
.convert()
.with(UniHelper::toFuture);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,19 @@ private DB2Pool initialize(VertxInternal vertx,
PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceReactiveRuntimeConfig);
DB2ConnectOptions db2ConnectOptions = toConnectOptions(dataSourceName, dataSourceRuntimeConfig,
dataSourceReactiveRuntimeConfig, dataSourceReactiveDB2Config);
Supplier<Future<DB2ConnectOptions>> databasesSupplier = toDatabasesSupplier(vertx, List.of(db2ConnectOptions),
Supplier<Future<DB2ConnectOptions>> databasesSupplier = toDatabasesSupplier(List.of(db2ConnectOptions),
dataSourceRuntimeConfig);
return createPool(vertx, poolOptions, db2ConnectOptions, dataSourceName, databasesSupplier, context);
}

private Supplier<Future<DB2ConnectOptions>> toDatabasesSupplier(Vertx vertx, List<DB2ConnectOptions> db2ConnectOptionsList,
private Supplier<Future<DB2ConnectOptions>> toDatabasesSupplier(List<DB2ConnectOptions> db2ConnectOptionsList,
DataSourceRuntimeConfig dataSourceRuntimeConfig) {
Supplier<Future<DB2ConnectOptions>> supplier;
if (dataSourceRuntimeConfig.credentialsProvider().isPresent()) {
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
supplier = new ConnectOptionsSupplier<>(vertx, credentialsProvider, name, db2ConnectOptionsList,
supplier = new ConnectOptionsSupplier<>(credentialsProvider, name, db2ConnectOptionsList,
DB2ConnectOptions::new);
} else {
supplier = Utils.roundRobinSupplier(db2ConnectOptionsList);
Expand Down Expand Up @@ -212,7 +212,7 @@ private DB2ConnectOptions toConnectOptions(String dataSourceName, DataSourceRunt
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
if (user != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,19 @@ private MSSQLPool initialize(VertxInternal vertx,
PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceReactiveRuntimeConfig);
MSSQLConnectOptions mssqlConnectOptions = toMSSQLConnectOptions(dataSourceName, dataSourceRuntimeConfig,
dataSourceReactiveRuntimeConfig, dataSourceReactiveMSSQLConfig);
Supplier<Future<MSSQLConnectOptions>> databasesSupplier = toDatabasesSupplier(vertx, List.of(mssqlConnectOptions),
Supplier<Future<MSSQLConnectOptions>> databasesSupplier = toDatabasesSupplier(List.of(mssqlConnectOptions),
dataSourceRuntimeConfig);
return createPool(vertx, poolOptions, mssqlConnectOptions, dataSourceName, databasesSupplier, context);
}

private Supplier<Future<MSSQLConnectOptions>> toDatabasesSupplier(Vertx vertx,
List<MSSQLConnectOptions> mssqlConnectOptionsList,
private Supplier<Future<MSSQLConnectOptions>> toDatabasesSupplier(List<MSSQLConnectOptions> mssqlConnectOptionsList,
DataSourceRuntimeConfig dataSourceRuntimeConfig) {
Supplier<Future<MSSQLConnectOptions>> supplier;
if (dataSourceRuntimeConfig.credentialsProvider().isPresent()) {
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
supplier = new ConnectOptionsSupplier<>(vertx, credentialsProvider, name, mssqlConnectOptionsList,
supplier = new ConnectOptionsSupplier<>(credentialsProvider, name, mssqlConnectOptionsList,
MSSQLConnectOptions::new);
} else {
supplier = Utils.roundRobinSupplier(mssqlConnectOptionsList);
Expand Down Expand Up @@ -214,7 +213,7 @@ private MSSQLConnectOptions toMSSQLConnectOptions(String dataSourceName, DataSou
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
if (user != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,19 @@ private MySQLPool initialize(VertxInternal vertx,
dataSourceReactiveMySQLConfig);
List<MySQLConnectOptions> mySQLConnectOptions = toMySQLConnectOptions(dataSourceName, dataSourceRuntimeConfig,
dataSourceReactiveRuntimeConfig, dataSourceReactiveMySQLConfig);
Supplier<Future<MySQLConnectOptions>> databasesSupplier = toDatabasesSupplier(vertx, mySQLConnectOptions,
Supplier<Future<MySQLConnectOptions>> databasesSupplier = toDatabasesSupplier(mySQLConnectOptions,
dataSourceRuntimeConfig);
return createPool(vertx, poolOptions, mySQLConnectOptions, dataSourceName, databasesSupplier, context);
}

private Supplier<Future<MySQLConnectOptions>> toDatabasesSupplier(Vertx vertx,
List<MySQLConnectOptions> mySQLConnectOptions,
private Supplier<Future<MySQLConnectOptions>> toDatabasesSupplier(List<MySQLConnectOptions> mySQLConnectOptions,
DataSourceRuntimeConfig dataSourceRuntimeConfig) {
Supplier<Future<MySQLConnectOptions>> supplier;
if (dataSourceRuntimeConfig.credentialsProvider().isPresent()) {
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
supplier = new ConnectOptionsSupplier<>(vertx, credentialsProvider, name, mySQLConnectOptions,
supplier = new ConnectOptionsSupplier<>(credentialsProvider, name, mySQLConnectOptions,
MySQLConnectOptions::new);
} else {
supplier = Utils.roundRobinSupplier(mySQLConnectOptions);
Expand Down Expand Up @@ -215,7 +214,7 @@ private List<MySQLConnectOptions> toMySQLConnectOptions(String dataSourceName,
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
if (user != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,19 @@ private OraclePool initialize(VertxInternal vertx,
PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceReactiveRuntimeConfig);
OracleConnectOptions oracleConnectOptions = toOracleConnectOptions(dataSourceName, dataSourceRuntimeConfig,
dataSourceReactiveRuntimeConfig, dataSourceReactiveOracleConfig);
Supplier<Future<OracleConnectOptions>> databasesSupplier = toDatabasesSupplier(vertx, List.of(oracleConnectOptions),
Supplier<Future<OracleConnectOptions>> databasesSupplier = toDatabasesSupplier(List.of(oracleConnectOptions),
dataSourceRuntimeConfig);
return createPool(vertx, poolOptions, oracleConnectOptions, dataSourceName, databasesSupplier, context);
}

private Supplier<Future<OracleConnectOptions>> toDatabasesSupplier(Vertx vertx,
List<OracleConnectOptions> oracleConnectOptions,
private Supplier<Future<OracleConnectOptions>> toDatabasesSupplier(List<OracleConnectOptions> oracleConnectOptions,
DataSourceRuntimeConfig dataSourceRuntimeConfig) {
Supplier<Future<OracleConnectOptions>> supplier;
if (dataSourceRuntimeConfig.credentialsProvider().isPresent()) {
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
supplier = new ConnectOptionsSupplier<>(vertx, credentialsProvider, name, oracleConnectOptions,
supplier = new ConnectOptionsSupplier<>(credentialsProvider, name, oracleConnectOptions,
OracleConnectOptions::new);
} else {
supplier = Utils.roundRobinSupplier(oracleConnectOptions);
Expand Down Expand Up @@ -205,7 +204,7 @@ private OracleConnectOptions toOracleConnectOptions(String dataSourceName, DataS
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
if (user != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,19 @@ private PgPool initialize(VertxInternal vertx,
PoolOptions poolOptions = toPoolOptions(eventLoopCount, dataSourceReactiveRuntimeConfig);
List<PgConnectOptions> pgConnectOptionsList = toPgConnectOptions(dataSourceName, dataSourceRuntimeConfig,
dataSourceReactiveRuntimeConfig, dataSourceReactivePostgreSQLConfig);
Supplier<Future<PgConnectOptions>> databasesSupplier = toDatabasesSupplier(vertx, pgConnectOptionsList,
Supplier<Future<PgConnectOptions>> databasesSupplier = toDatabasesSupplier(pgConnectOptionsList,
dataSourceRuntimeConfig);
return createPool(vertx, poolOptions, pgConnectOptionsList, dataSourceName, databasesSupplier, context);
}

private Supplier<Future<PgConnectOptions>> toDatabasesSupplier(Vertx vertx, List<PgConnectOptions> pgConnectOptionsList,
private Supplier<Future<PgConnectOptions>> toDatabasesSupplier(List<PgConnectOptions> pgConnectOptionsList,
DataSourceRuntimeConfig dataSourceRuntimeConfig) {
Supplier<Future<PgConnectOptions>> supplier;
if (dataSourceRuntimeConfig.credentialsProvider().isPresent()) {
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
supplier = new ConnectOptionsSupplier<>(vertx, credentialsProvider, name, pgConnectOptionsList,
supplier = new ConnectOptionsSupplier<>(credentialsProvider, name, pgConnectOptionsList,
PgConnectOptions::new);
} else {
supplier = Utils.roundRobinSupplier(pgConnectOptionsList);
Expand Down Expand Up @@ -206,7 +206,7 @@ private List<PgConnectOptions> toPgConnectOptions(String dataSourceName, DataSou
String beanName = dataSourceRuntimeConfig.credentialsProviderName().orElse(null);
CredentialsProvider credentialsProvider = CredentialsProviderFinder.find(beanName);
String name = dataSourceRuntimeConfig.credentialsProvider().get();
Map<String, String> credentials = credentialsProvider.getCredentials(name);
Map<String, String> credentials = credentialsProvider.getCredentialsAsync(name).await().indefinitely();
String user = credentials.get(USER_PROPERTY_NAME);
String password = credentials.get(PASSWORD_PROPERTY_NAME);
if (user != null) {
Expand Down
Loading
Loading