properties();
/**
@@ -97,6 +101,7 @@ public interface DevServicesBuildTimeConfig {
*
* This has no effect if the provider is not a container-based database, such as H2 or Derby.
*/
+ @ConfigDocMapKey("host-path")
Map volumes();
/**
diff --git a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchDevServicesBuildTimeConfig.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchDevServicesBuildTimeConfig.java
index f468715df9cba..b5b1625001413 100644
--- a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchDevServicesBuildTimeConfig.java
+++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/ElasticsearchDevServicesBuildTimeConfig.java
@@ -4,6 +4,7 @@
import java.util.Objects;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
@@ -96,6 +97,7 @@ public class ElasticsearchDevServicesBuildTimeConfig {
* Environment variables that are passed to the container.
*/
@ConfigItem
+ @ConfigDocMapKey("environment-variable-name")
public Map containerEnv;
/**
diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java
index 2e9d9aa3dd35e..772ba52458280 100644
--- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java
+++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java
@@ -7,6 +7,7 @@
import java.util.Optional;
import java.util.OptionalInt;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -218,6 +219,7 @@ public static FlywayDataSourceRuntimeConfig defaultConfig() {
* Sets the placeholders to replace in SQL migration scripts.
*/
@ConfigItem
+ @ConfigDocMapKey("placeholder-key")
public Map placeholders = Collections.emptyMap();
/**
diff --git a/extensions/hibernate-orm/deployment-spi/src/main/java/io/quarkus/hibernate/orm/deployment/spi/DatabaseKindDialectBuildItem.java b/extensions/hibernate-orm/deployment-spi/src/main/java/io/quarkus/hibernate/orm/deployment/spi/DatabaseKindDialectBuildItem.java
index fe807866175cf..0087e4334b4f7 100644
--- a/extensions/hibernate-orm/deployment-spi/src/main/java/io/quarkus/hibernate/orm/deployment/spi/DatabaseKindDialectBuildItem.java
+++ b/extensions/hibernate-orm/deployment-spi/src/main/java/io/quarkus/hibernate/orm/deployment/spi/DatabaseKindDialectBuildItem.java
@@ -1,6 +1,7 @@
package io.quarkus.hibernate.orm.deployment.spi;
import java.util.Optional;
+import java.util.Set;
import io.quarkus.builder.item.MultiBuildItem;
@@ -9,15 +10,74 @@
*/
public final class DatabaseKindDialectBuildItem extends MultiBuildItem {
private final String dbKind;
- private final String dialect;
+ private final Optional databaseProductName;
+ private final Optional dialect;
+ private final Set matchingDialects;
private final Optional defaultDatabaseProductVersion;
+ /**
+ * @param dbKind The DB Kind set through {@code quarkus.datasource.db-kind}
+ * @param databaseProductName The corresponding database-product-name to set in Hibernate ORM.
+ * See {@code org.hibernate.dialect.Database} for information on how this name is resolved to a dialect.
+ * @param dialects The corresponding dialects in Hibernate ORM,
+ * to detect the dbKind when using database multi-tenancy.
+ */
+ public static DatabaseKindDialectBuildItem forCoreDialect(String dbKind, String databaseProductName,
+ Set dialects) {
+ return new DatabaseKindDialectBuildItem(dbKind, Optional.empty(), Optional.of(databaseProductName),
+ dialects, Optional.empty());
+ }
+
+ /**
+ * @param dbKind The DB Kind set through {@code quarkus.datasource.db-kind}
+ * @param databaseProductName The corresponding database-product-name to set in Hibernate ORM.
+ * See {@code org.hibernate.dialect.Database} for information on how this name is resolved to a dialect.
+ * @param dialects The corresponding dialects in Hibernate ORM,
+ * to detect the dbKind when using database multi-tenancy.
+ * @param defaultDatabaseProductVersion The default database-product-version to set in Hibernate ORM.
+ * This is useful when the default version of the dialect in Hibernate ORM
+ * is lower than what we expect in Quarkus.
+ */
+ public static DatabaseKindDialectBuildItem forCoreDialect(String dbKind, String databaseProductName,
+ Set dialects, String defaultDatabaseProductVersion) {
+ return new DatabaseKindDialectBuildItem(dbKind, Optional.empty(), Optional.of(databaseProductName),
+ dialects, Optional.of(defaultDatabaseProductVersion));
+ }
+
+ /**
+ * @param dbKind The DB Kind set through {@code quarkus.datasource.db-kind}
+ * @param dialect The corresponding dialect to set in Hibernate ORM.
+ * See {@code org.hibernate.dialect.Database} for information on how this name is resolved to a dialect.
+ */
+ public static DatabaseKindDialectBuildItem forThirdPartyDialect(String dbKind, String dialect) {
+ return new DatabaseKindDialectBuildItem(dbKind, Optional.of(dialect), Optional.empty(), Set.of(dialect),
+ Optional.empty());
+ }
+
/**
* @param dbKind The DB Kind set through {@code quarkus.datasource.db-kind}
* @param dialect The corresponding dialect to set in Hibernate ORM.
+ * See {@code org.hibernate.dialect.Database} for information on how this name is resolved to a dialect.
+ * @param defaultDatabaseProductVersion The default database-product-version to set in Hibernate ORM.
+ * This is useful when the default version of the dialect in Hibernate ORM
+ * is lower than what we expect in Quarkus.
*/
+ public static DatabaseKindDialectBuildItem forThirdPartyDialect(String dbKind, String dialect,
+ String defaultDatabaseProductVersion) {
+ return new DatabaseKindDialectBuildItem(dbKind, Optional.of(dialect), Optional.empty(),
+ Set.of(dialect), Optional.of(defaultDatabaseProductVersion));
+ }
+
+ /**
+ * @param dbKind The DB Kind set through {@code quarkus.datasource.db-kind}
+ * @param dialect The corresponding dialect to set in Hibernate ORM.
+ * @deprecated Use {@link #forCoreDialect(String, String, Set)}(different arguments!)
+ * for core Hibernate ORM dialects to avoid warnings on startup,
+ * or {@link #forThirdPartyDialect(String, String)} for community or third-party dialects.
+ */
+ @Deprecated
public DatabaseKindDialectBuildItem(String dbKind, String dialect) {
- this(dbKind, dialect, Optional.empty());
+ this(dbKind, Optional.of(dialect), Optional.empty(), Set.of(dialect), Optional.empty());
}
/**
@@ -27,15 +87,22 @@ public DatabaseKindDialectBuildItem(String dbKind, String dialect) {
* @param defaultDatabaseProductVersion The default database-product-version to set in Hibernate ORM.
* This is useful when the default version of the dialect in Hibernate ORM
* is lower than what we expect in Quarkus.
+ * @deprecated Use {@link #forCoreDialect(String, String, Set, String)}(different arguments!)
+ * for core Hibernate ORM dialects to avoid warnings on startup,
+ * or {@link #forThirdPartyDialect(String, String, String)} for community or third-party dialects.
*/
+ @Deprecated
public DatabaseKindDialectBuildItem(String dbKind, String dialect, String defaultDatabaseProductVersion) {
- this(dbKind, dialect, Optional.of(defaultDatabaseProductVersion));
+ this(dbKind, Optional.of(dialect), Optional.empty(), Set.of(dialect), Optional.of(defaultDatabaseProductVersion));
}
- private DatabaseKindDialectBuildItem(String dbKind, String dialect,
+ private DatabaseKindDialectBuildItem(String dbKind, Optional dialect,
+ Optional databaseProductName, Set matchingDialects,
Optional defaultDatabaseProductVersion) {
this.dbKind = dbKind;
this.dialect = dialect;
+ this.matchingDialects = matchingDialects;
+ this.databaseProductName = databaseProductName;
this.defaultDatabaseProductVersion = defaultDatabaseProductVersion;
}
@@ -44,9 +111,21 @@ public String getDbKind() {
}
public String getDialect() {
+ return dialect.get();
+ }
+
+ public Optional getDialectOptional() {
return dialect;
}
+ public Set getMatchingDialects() {
+ return matchingDialects;
+ }
+
+ public Optional getDatabaseProductName() {
+ return databaseProductName;
+ }
+
public Optional getDefaultDatabaseProductVersion() {
return defaultDatabaseProductVersion;
}
diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java
index 25d5e14f47844..2dffe9d53f761 100644
--- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java
+++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateLogFilterBuildStep.java
@@ -30,8 +30,6 @@ void setupLogFilters(BuildProducer filters) {
// Silence incubating settings warnings as we will use some for compatibility
filters.produce(new LogCleanupFilterBuildItem("org.hibernate.orm.incubating",
"HHH90006001"));
- // https://hibernate.atlassian.net/browse/HHH-16546
- filters.produce(new LogCleanupFilterBuildItem("org.hibernate.tuple.entity.EntityMetamodel", "HHH000157"));
//This "deprecation" warning isn't practical for the specific Quarkus needs, as it reminds users they don't need
//to set the 'hibernate.dialect' property, however it's being set by Quarkus buildsteps so they can't avoid it.
diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java
index bdf64462f2836..1f29cbd83ca83 100644
--- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java
+++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java
@@ -229,7 +229,10 @@ public interface HibernateOrmConfigPersistenceUnit {
/**
* Defines the name of the datasource to use in case of SCHEMA approach. The datasource of the persistence unit will be used
* if not set.
+ *
+ * @deprecated Use {@link #datasource()} instead.
*/
+ @Deprecated
@WithConverter(TrimmedStringConverter.class)
Optional multitenantSchemaDatasource();
diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
index 54b5f4de5f5aa..656443066024a 100644
--- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
+++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
@@ -162,24 +162,27 @@ public final class HibernateOrmProcessor {
@BuildStep
void registerHibernateOrmMetadataForCoreDialects(
BuildProducer producer) {
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.DB2,
- "org.hibernate.dialect.DB2Dialect"));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.DERBY,
- "org.hibernate.dialect.DerbyDialect"));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.H2,
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.DB2, "DB2",
+ Set.of("org.hibernate.dialect.DB2Dialect")));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.DERBY, "Apache Derby",
+ Set.of("org.hibernate.dialect.DerbyDialect")));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.H2, "H2",
+ Set.of("org.hibernate.dialect.H2Dialect"),
// Using our own default version is extra important for H2
// See https://github.com/quarkusio/quarkus/issues/1886
- "org.hibernate.dialect.H2Dialect", DialectVersions.Defaults.H2));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.MARIADB,
- "org.hibernate.dialect.MariaDBDialect", DialectVersions.Defaults.MARIADB));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.MSSQL,
- "org.hibernate.dialect.SQLServerDialect", DialectVersions.Defaults.MSSQL));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.MYSQL,
- "org.hibernate.dialect.MySQLDialect"));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.ORACLE,
- "org.hibernate.dialect.OracleDialect"));
- producer.produce(new DatabaseKindDialectBuildItem(DatabaseKind.POSTGRESQL,
- "org.hibernate.dialect.PostgreSQLDialect"));
+ DialectVersions.Defaults.H2));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.MARIADB, "MariaDB",
+ Set.of("org.hibernate.dialect.MariaDBDialect"),
+ DialectVersions.Defaults.MARIADB));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.MSSQL, "Microsoft SQL Server",
+ Set.of("org.hibernate.dialect.SQLServerDialect"),
+ DialectVersions.Defaults.MSSQL));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.MYSQL, "MySQL",
+ Set.of("org.hibernate.dialect.MySQLDialect")));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.ORACLE, "Oracle",
+ Set.of("org.hibernate.dialect.OracleDialect")));
+ producer.produce(DatabaseKindDialectBuildItem.forCoreDialect(DatabaseKind.POSTGRESQL, "PostgreSQL",
+ Set.of("org.hibernate.dialect.PostgreSQLDialect")));
}
@BuildStep
@@ -667,33 +670,51 @@ public void multitenancy(HibernateOrmRecorder recorder,
boolean multitenancyEnabled = false;
for (PersistenceUnitDescriptorBuildItem persistenceUnitDescriptor : persistenceUnitDescriptors) {
- if (persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy() == MultiTenancyStrategy.NONE) {
- continue;
- }
+ String persistenceUnitConfigName = persistenceUnitDescriptor.getConfigurationName();
+ var multitenancyStrategy = persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy();
+ switch (multitenancyStrategy) {
+ case NONE -> {
+ }
+ case DISCRIMINATOR -> multitenancyEnabled = true;
+ case DATABASE, SCHEMA -> {
+ multitenancyEnabled = true;
+
+ String multiTenancySchemaDataSource = persistenceUnitDescriptor.getMultiTenancySchemaDataSource();
+ Optional datasource;
+ if (multitenancyStrategy == MultiTenancyStrategy.SCHEMA && multiTenancySchemaDataSource != null) {
+ LOG.warnf("Configuration property '%1$s' is deprecated. Use '%2$s' instead.",
+ HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitConfigName,
+ "multitenant-schema-datasource"),
+ HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitConfigName, "datasource"));
+ datasource = Optional.of(multiTenancySchemaDataSource);
+ } else {
+ datasource = persistenceUnitDescriptor.getConfig().getDataSource();
+ }
- multitenancyEnabled = true;
-
- ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem.configure(DataSourceTenantConnectionResolver.class)
- .scope(ApplicationScoped.class)
- .types(TenantConnectionResolver.class)
- .setRuntimeInit()
- .defaultBean()
- .unremovable()
- .supplier(recorder.dataSourceTenantConnectionResolver(persistenceUnitDescriptor.getPersistenceUnitName(),
- persistenceUnitDescriptor.getConfig().getDataSource(),
- persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy(),
- persistenceUnitDescriptor.getMultiTenancySchemaDataSource()));
-
- if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitDescriptor.getPersistenceUnitName())) {
- configurator.addQualifier(Default.class);
- } else {
- configurator.addQualifier().annotation(DotNames.NAMED)
- .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
- configurator.addQualifier().annotation(PersistenceUnit.class)
- .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
- }
+ ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
+ .configure(DataSourceTenantConnectionResolver.class)
+ .scope(ApplicationScoped.class)
+ .types(TenantConnectionResolver.class)
+ .setRuntimeInit()
+ .defaultBean()
+ .unremovable()
+ .supplier(recorder.dataSourceTenantConnectionResolver(
+ persistenceUnitDescriptor.getPersistenceUnitName(),
+ datasource,
+ persistenceUnitDescriptor.getConfig().getMultiTenancyStrategy()));
+
+ if (PersistenceUnitUtil.isDefaultPersistenceUnit(persistenceUnitDescriptor.getPersistenceUnitName())) {
+ configurator.addQualifier(Default.class);
+ } else {
+ configurator.addQualifier().annotation(DotNames.NAMED)
+ .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
+ configurator.addQualifier().annotation(PersistenceUnit.class)
+ .addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
+ }
- syntheticBeans.produce(configurator.done());
+ syntheticBeans.produce(configurator.done());
+ }
+ }
}
if (multitenancyEnabled) {
@@ -1107,15 +1128,18 @@ private static void collectDialectConfig(String persistenceUnitName,
}
Optional dialect = explicitDialect;
+ Optional dbProductName = Optional.empty();
Optional dbProductVersion = explicitDbMinVersion;
if (dbKind.isPresent() || explicitDialect.isPresent()) {
for (DatabaseKindDialectBuildItem item : dbKindMetadataBuildItems) {
if (dbKind.isPresent() && DatabaseKind.is(dbKind.get(), item.getDbKind())
// Set the default version based on the dialect when we don't have a datasource
// (i.e. for database multi-tenancy)
- || explicitDialect.isPresent() && explicitDialect.get().equals(item.getDialect())) {
- if (explicitDialect.isEmpty()) {
- dialect = Optional.of(item.getDialect());
+ || explicitDialect.isPresent() && item.getMatchingDialects().contains(explicitDialect.get())) {
+ dbProductName = item.getDatabaseProductName();
+ if (dbProductName.isEmpty() && explicitDialect.isEmpty()) {
+ // Use dialects only as a last resort, prefer product name or explicitly user-provided dialect
+ dialect = item.getDialectOptional();
}
if (explicitDbMinVersion.isEmpty()) {
dbProductVersion = item.getDefaultDatabaseProductVersion();
@@ -1123,7 +1147,7 @@ private static void collectDialectConfig(String persistenceUnitName,
break;
}
}
- if (dialect.isEmpty()) {
+ if (dialect.isEmpty() && dbProductName.isEmpty()) {
throw new ConfigurationException(
"The Hibernate ORM extension could not guess the dialect from the database kind '" + dbKind.get()
+ "'. Add an explicit '"
@@ -1134,6 +1158,8 @@ private static void collectDialectConfig(String persistenceUnitName,
if (dialect.isPresent()) {
puPropertiesCollector.accept(AvailableSettings.DIALECT, dialect.get());
+ } else if (dbProductName.isPresent()) {
+ puPropertiesCollector.accept(AvailableSettings.JAKARTA_HBM2DDL_DB_NAME, dbProductName.get());
} else {
// We only get here with the database multi-tenancy strategy; see the initial check, up top.
assert multiTenancyStrategy == MultiTenancyStrategy.DATABASE;
@@ -1148,7 +1174,7 @@ private static void collectDialectConfig(String persistenceUnitName,
if (persistenceUnitConfig.dialect().storageEngine().isPresent()) {
// Only actually set the storage engines if MySQL or MariaDB
- if (isMySQLOrMariaDB(dialect.get())) {
+ if (isMySQLOrMariaDB(dbKind, dialect)) {
// The storage engine has to be set as a system property.
// We record it so that we can later run checks (because we can only set a single value)
storageEngineCollector.add(persistenceUnitConfig.dialect().storageEngine().get());
@@ -1609,9 +1635,15 @@ private static Class[] toArray(final Set> interfaces) {
return interfaces.toArray(new Class[interfaces.size()]);
}
- private static boolean isMySQLOrMariaDB(String dialect) {
- String lowercaseDialect = dialect.toLowerCase(Locale.ROOT);
- return lowercaseDialect.contains("mysql") || lowercaseDialect.contains("mariadb");
+ private static boolean isMySQLOrMariaDB(Optional dbKind, Optional dialect) {
+ if (dbKind.isPresent() && (DatabaseKind.isMySQL(dbKind.get()) || DatabaseKind.isMariaDB(dbKind.get()))) {
+ return true;
+ }
+ if (dialect.isPresent()) {
+ String lowercaseDialect = dialect.get().toLowerCase(Locale.ROOT);
+ return lowercaseDialect.contains("mysql") || lowercaseDialect.contains("mariadb");
+ }
+ return false;
}
private static final class ProxyCache {
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java
index 5050d7e5c648e..01259ea60f49c 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java
@@ -83,12 +83,11 @@ public void created(BeanContainer beanContainer) {
public Supplier dataSourceTenantConnectionResolver(String persistenceUnitName,
Optional dataSourceName,
- MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSourceName) {
+ MultiTenancyStrategy multiTenancyStrategy) {
return new Supplier() {
@Override
public DataSourceTenantConnectionResolver get() {
- return new DataSourceTenantConnectionResolver(persistenceUnitName, dataSourceName, multiTenancyStrategy,
- multiTenancySchemaDataSourceName);
+ return new DataSourceTenantConnectionResolver(persistenceUnitName, dataSourceName, multiTenancyStrategy);
}
};
}
diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java
index 29b38ebebadbe..4bc6dbed953d0 100644
--- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java
+++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/tenant/DataSourceTenantConnectionResolver.java
@@ -11,7 +11,6 @@
import org.jboss.logging.Logger;
import io.agroal.api.AgroalDataSource;
-import io.agroal.api.configuration.AgroalDataSourceConfiguration;
import io.quarkus.agroal.DataSource;
import io.quarkus.arc.Arc;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
@@ -35,17 +34,14 @@ public class DataSourceTenantConnectionResolver implements TenantConnectionResol
private MultiTenancyStrategy multiTenancyStrategy;
- private String multiTenancySchemaDataSourceName;
-
public DataSourceTenantConnectionResolver() {
}
public DataSourceTenantConnectionResolver(String persistenceUnitName, Optional dataSourceName,
- MultiTenancyStrategy multiTenancyStrategy, String multiTenancySchemaDataSourceName) {
+ MultiTenancyStrategy multiTenancyStrategy) {
this.persistenceUnitName = persistenceUnitName;
this.dataSourceName = dataSourceName;
this.multiTenancyStrategy = multiTenancyStrategy;
- this.multiTenancySchemaDataSourceName = multiTenancySchemaDataSourceName;
}
@Override
@@ -53,49 +49,28 @@ public ConnectionProvider resolve(String tenantId) {
LOG.debugv("resolve((persistenceUnitName={0}, tenantIdentifier={1})", persistenceUnitName, tenantId);
LOG.debugv("multitenancy strategy: {0}", multiTenancyStrategy);
- AgroalDataSource dataSource = tenantDataSource(dataSourceName, tenantId, multiTenancyStrategy,
- multiTenancySchemaDataSourceName);
+ AgroalDataSource dataSource = tenantDataSource(dataSourceName, tenantId, multiTenancyStrategy);
if (dataSource == null) {
throw new IllegalStateException(
String.format(Locale.ROOT, "No instance of datasource found for persistence unit '%1$s' and tenant '%2$s'",
persistenceUnitName, tenantId));
}
- if (multiTenancyStrategy == MultiTenancyStrategy.SCHEMA) {
- return new SchemaTenantConnectionProvider(tenantId, dataSource);
- }
- return new QuarkusConnectionProvider(dataSource);
- }
-
- /**
- * Create a new data source from the given configuration.
- *
- * @param config Configuration to use.
- *
- * @return New data source instance.
- */
- private static AgroalDataSource createFrom(AgroalDataSourceConfiguration config) {
- try {
- return AgroalDataSource.from(config);
- } catch (SQLException ex) {
- throw new IllegalStateException("Failed to create a new data source based on the existing datasource configuration",
- ex);
- }
+ return switch (multiTenancyStrategy) {
+ case DATABASE -> new QuarkusConnectionProvider(dataSource);
+ case SCHEMA -> new SchemaTenantConnectionProvider(tenantId, dataSource);
+ default -> throw new IllegalStateException("Unexpected multitenancy strategy: " + multiTenancyStrategy);
+ };
}
private static AgroalDataSource tenantDataSource(Optional dataSourceName, String tenantId,
- MultiTenancyStrategy strategy, String multiTenancySchemaDataSourceName) {
- if (strategy != MultiTenancyStrategy.SCHEMA) {
- return Arc.container().instance(AgroalDataSource.class, new DataSource.DataSourceLiteral(tenantId)).get();
- }
-
- if (multiTenancySchemaDataSourceName == null) {
- // The datasource name should always be present when using SCHEMA multi-tenancy;
+ MultiTenancyStrategy strategy) {
+ return switch (strategy) {
+ case DATABASE -> Arc.container().instance(AgroalDataSource.class, new DataSource.DataSourceLiteral(tenantId)).get();
+ // The datasource name should always be present when using a multi-tenancy other than DATABASE;
// we perform checks in HibernateOrmProcessor during the build.
- AgroalDataSource dataSource = getDataSource(dataSourceName.get());
- return createFrom(dataSource.getConfiguration());
- }
-
- return getDataSource(multiTenancySchemaDataSourceName);
+ case SCHEMA -> getDataSource(dataSourceName.get());
+ default -> throw new IllegalStateException("Unexpected multitenancy strategy: " + strategy);
+ };
}
private static AgroalDataSource getDataSource(String dataSourceName) {
diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/Dialects.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/Dialects.java
deleted file mode 100644
index a699c1fabea6d..0000000000000
--- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/Dialects.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package io.quarkus.hibernate.reactive.deployment;
-
-import java.util.List;
-
-import io.quarkus.datasource.common.runtime.DatabaseKind;
-import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem;
-import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig;
-import io.quarkus.runtime.configuration.ConfigurationException;
-
-/**
- * This used to be the approach before 6bf38240 in the Hibernate ORM extension as well.
- * Align to ORM? TBD
- */
-@Deprecated
-final class Dialects {
-
- private Dialects() {
- //utility
- }
-
- public static String guessDialect(String persistenceUnitName, String resolvedDbKind,
- List dbKindDialectBuildItems) {
- for (DatabaseKindDialectBuildItem item : dbKindDialectBuildItems) {
- if (DatabaseKind.is(resolvedDbKind, item.getDbKind())) {
- return item.getDialect();
- }
- }
-
- String error = "The Hibernate ORM extension could not guess the dialect from the database kind '" + resolvedDbKind
- + "'. Add an explicit '" + HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitName, "dialect")
- + "' property.";
- throw new ConfigurationException(error);
- }
-}
diff --git a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java
index 37303a0f579a1..cd55a007cab70 100644
--- a/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java
+++ b/extensions/hibernate-reactive/deployment/src/main/java/io/quarkus/hibernate/reactive/deployment/HibernateReactiveProcessor.java
@@ -427,21 +427,24 @@ private static ParsedPersistenceXmlDescriptor generateReactivePersistenceUnit(
return desc;
}
- private static void setDialectAndStorageEngine(Optional dbKindOptional, Optional explicitDialect,
+ private static void setDialectAndStorageEngine(Optional dbKind, Optional explicitDialect,
Optional explicitDbMinVersion, List dbKindDialectBuildItems,
Optional storageEngine, BuildProducer systemProperties,
ParsedPersistenceXmlDescriptor desc) {
final String persistenceUnitName = DEFAULT_PERSISTENCE_UNIT_NAME;
Optional dialect = explicitDialect;
+ Optional dbProductName = Optional.empty();
Optional dbProductVersion = explicitDbMinVersion;
- if (dbKindOptional.isPresent() || explicitDialect.isPresent()) {
+ if (dbKind.isPresent() || explicitDialect.isPresent()) {
for (DatabaseKindDialectBuildItem item : dbKindDialectBuildItems) {
- if (dbKindOptional.isPresent() && DatabaseKind.is(dbKindOptional.get(), item.getDbKind())
+ if (dbKind.isPresent() && DatabaseKind.is(dbKind.get(), item.getDbKind())
// Set the default version based on the dialect when we don't have a datasource
// (i.e. for database multi-tenancy)
- || explicitDialect.isPresent() && explicitDialect.get().equals(item.getDialect())) {
- if (explicitDialect.isEmpty()) {
- dialect = Optional.of(item.getDialect());
+ || explicitDialect.isPresent() && item.getMatchingDialects().contains(explicitDialect.get())) {
+ dbProductName = item.getDatabaseProductName();
+ if (dbProductName.isEmpty() && explicitDialect.isEmpty()) {
+ // Use dialects only as a last resort, prefer product name or explicitly user-provided dialect
+ dialect = item.getDialectOptional();
}
if (explicitDbMinVersion.isEmpty()) {
dbProductVersion = item.getDefaultDatabaseProductVersion();
@@ -449,10 +452,10 @@ private static void setDialectAndStorageEngine(Optional dbKindOptional,
break;
}
}
- if (dialect.isEmpty()) {
+ if (dialect.isEmpty() && dbProductName.isEmpty()) {
throw new ConfigurationException(
"The Hibernate Reactive extension could not guess the dialect from the database kind '"
- + dbKindOptional.get()
+ + dbKind.get()
+ "'. Add an explicit '"
+ HibernateOrmRuntimeConfig.puPropertyKey(persistenceUnitName, "dialect")
+ "' property.");
@@ -461,6 +464,8 @@ private static void setDialectAndStorageEngine(Optional dbKindOptional,
if (dialect.isPresent()) {
desc.getProperties().setProperty(AvailableSettings.DIALECT, dialect.get());
+ } else if (dbProductName.isPresent()) {
+ desc.getProperties().setProperty(AvailableSettings.JAKARTA_HBM2DDL_DB_NAME, dbProductName.get());
} else {
// We only get here with the database multi-tenancy strategy; see the initial check, up top.
throw new ConfigurationException(String.format(Locale.ROOT,
@@ -472,15 +477,11 @@ private static void setDialectAndStorageEngine(Optional dbKindOptional,
persistenceUnitName));
}
- if (dbProductVersion.isPresent()) {
- desc.getProperties().setProperty(JAKARTA_HBM2DDL_DB_VERSION, dbProductVersion.get());
- }
-
// The storage engine has to be set as a system property.
if (storageEngine.isPresent()) {
systemProperties.produce(new SystemPropertyBuildItem(STORAGE_ENGINE, storageEngine.get()));
// Only actually set the storage engines if MySQL or MariaDB
- if (isMySQLOrMariaDB(dialect.get())) {
+ if (isMySQLOrMariaDB(dbKind, dialect)) {
systemProperties.produce(new SystemPropertyBuildItem(STORAGE_ENGINE, storageEngine.get()));
} else {
LOG.warnf("The storage engine set through configuration property '%1$s' is being ignored"
@@ -489,11 +490,20 @@ private static void setDialectAndStorageEngine(Optional dbKindOptional,
}
}
+ if (dbProductVersion.isPresent()) {
+ desc.getProperties().setProperty(JAKARTA_HBM2DDL_DB_VERSION, dbProductVersion.get());
+ }
}
- private static boolean isMySQLOrMariaDB(String dialect) {
- String lowercaseDialect = dialect.toLowerCase(Locale.ROOT);
- return lowercaseDialect.contains("mysql") || lowercaseDialect.contains("mariadb");
+ private static boolean isMySQLOrMariaDB(Optional dbKind, Optional dialect) {
+ if (dbKind.isPresent() && (DatabaseKind.isMySQL(dbKind.get()) || DatabaseKind.isMariaDB(dbKind.get()))) {
+ return true;
+ }
+ if (dialect.isPresent()) {
+ String lowercaseDialect = dialect.get().toLowerCase(Locale.ROOT);
+ return lowercaseDialect.contains("mysql") || lowercaseDialect.contains("mariadb");
+ }
+ return false;
}
private static void setMaxFetchDepth(ParsedPersistenceXmlDescriptor descriptor, OptionalInt maxFetchDepth) {
diff --git a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java
index 83f70143409bd..e4368b0126886 100644
--- a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java
+++ b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java
@@ -6,6 +6,7 @@
import java.util.Optional;
import java.util.OptionalInt;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -124,6 +125,7 @@ public class InfinispanDevServicesConfig {
* Environment variables that are passed to the container.
*/
@ConfigItem
+ @ConfigDocMapKey("environment-variable-name")
public Map containerEnv;
/**
diff --git a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoBuildTimeConfig.java b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoBuildTimeConfig.java
index 4990353262920..8dd651ba25ad4 100644
--- a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoBuildTimeConfig.java
+++ b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoBuildTimeConfig.java
@@ -2,6 +2,7 @@
import java.util.Map;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
@@ -76,6 +77,7 @@ interface Build {
* Additional properties to be added to the build section
*/
@WithParentName
+ @ConfigDocMapKey("property-key")
Map additionalProperties();
}
diff --git a/extensions/info/deployment/src/main/resources/dev-ui/qwc-info.js b/extensions/info/deployment/src/main/resources/dev-ui/qwc-info.js
index 5ba4c71f05d00..a89b011d4f67b 100644
--- a/extensions/info/deployment/src/main/resources/dev-ui/qwc-info.js
+++ b/extensions/info/deployment/src/main/resources/dev-ui/qwc-info.js
@@ -3,7 +3,7 @@ import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import { columnBodyRenderer } from '@vaadin/grid/lit.js';
import { infoUrl } from 'build-time-data';
import '@vaadin/progress-bar';
-import 'qui-card';
+import '@qomponent/qui-card';
import '@vaadin/icon';
/**
@@ -83,7 +83,7 @@ export class QwcInfo extends LitElement {
_renderOsInfo(info){
if(info.os){
let os = info.os;
- return html`
+ return html`
${this._renderOsIcon(os.name)}
@@ -99,7 +99,7 @@ export class QwcInfo extends LitElement {
_renderJavaInfo(info){
if(info.java){
let java = info.java;
- return html`
+ return html`
@@ -126,7 +126,7 @@ export class QwcInfo extends LitElement {
_renderGitInfo(info){
if(info.git){
let git = info.git;
- return html`
+ return html`
@@ -162,7 +162,7 @@ export class QwcInfo extends LitElement {
_renderBuildInfo(info){
if(info.build){
let build = info.build;
- return html`
+ return html`
${build.group} |
@@ -189,7 +189,7 @@ export class QwcInfo extends LitElement {
for (const property of Object.keys(extInfo)){
rows.push(html`${extInfo[property]} |
`);
}
- cards.push(html`
+ cards.push(html`
@@ -202,4 +202,4 @@ export class QwcInfo extends LitElement {
}
}
}
-customElements.define('qwc-info', QwcInfo);
\ No newline at end of file
+customElements.define('qwc-info', QwcInfo);
diff --git a/extensions/jackson/deployment/src/test/java/io/quarkus/jackson/deployment/JacksonDefaultPoolTest.java b/extensions/jackson/deployment/src/test/java/io/quarkus/jackson/deployment/JacksonDefaultPoolTest.java
deleted file mode 100644
index 715f9d62ff3ba..0000000000000
--- a/extensions/jackson/deployment/src/test/java/io/quarkus/jackson/deployment/JacksonDefaultPoolTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.quarkus.jackson.deployment;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import com.fasterxml.jackson.core.util.JsonRecyclerPools;
-
-public class JacksonDefaultPoolTest {
-
- @Test
- public void validateDefaultJacksonPool() {
- Assertions.assertThat(JsonRecyclerPools.defaultPool()).isInstanceOf(JsonRecyclerPools.LockFreePool.class);
- }
-}
diff --git a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/VertxHybridPoolObjectMapperCustomizer.java b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/VertxHybridPoolObjectMapperCustomizer.java
index 3254837f6e54d..eb3bf36158d06 100644
--- a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/VertxHybridPoolObjectMapperCustomizer.java
+++ b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/VertxHybridPoolObjectMapperCustomizer.java
@@ -11,8 +11,9 @@ public class VertxHybridPoolObjectMapperCustomizer implements ObjectMapperCustom
@Override
public void customize(ObjectMapper objectMapper) {
var existingMapperPool = objectMapper.getFactory()._getRecyclerPool();
- // JsonRecyclerPools.defaultPool() by default should create a LockFreePool
- if (existingMapperPool instanceof JsonRecyclerPools.LockFreePool) {
+ // if the recycler pool in use is the default jackson one it means that user hasn't
+ // explicitly chosen any, so we can replace it with the vert.x virtual thread friendly one
+ if (existingMapperPool.getClass() == JsonRecyclerPools.defaultPool().getClass()) {
objectMapper.getFactory().setRecyclerPool(HybridJacksonPool.getInstance());
}
}
diff --git a/extensions/jdbc/jdbc-h2/runtime/pom.xml b/extensions/jdbc/jdbc-h2/runtime/pom.xml
index af1b7833ba93b..ef1752782a3aa 100644
--- a/extensions/jdbc/jdbc-h2/runtime/pom.xml
+++ b/extensions/jdbc/jdbc-h2/runtime/pom.xml
@@ -52,6 +52,9 @@
com.h2database:h2
+
+ io.quarkus.jdbc.h2
+
diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java
index 9bfd954d11e47..8167b900a952e 100644
--- a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java
+++ b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java
@@ -2,6 +2,7 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.AdditionalIndexedClassesBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
/**
@@ -14,7 +15,8 @@ public final class OracleNativeImage {
* by reflection, as commonly expected.
*/
@BuildStep
- void reflection(BuildProducer reflectiveClass) {
+ void reflection(BuildProducer reflectiveClass,
+ BuildProducer additionalIndexedClasses) {
//Not strictly necessary when using Agroal, as it also registers
//any JDBC driver being configured explicitly through its configuration.
//We register it for the sake of people not using Agroal.
@@ -23,6 +25,10 @@ void reflection(BuildProducer reflectiveClass) {
final String driverName = "oracle.jdbc.driver.OracleDriver";
reflectiveClass.produce(ReflectiveClassBuildItem.builder(driverName).build());
+ // This is needed when using XA and we use the `@RegisterForReflection` trick to make sure all nested classes are registered for reflection
+ additionalIndexedClasses
+ .produce(new AdditionalIndexedClassesBuildItem("io.quarkus.jdbc.oracle.runtime.graal.OracleReflections"));
+
// for ldap style jdbc urls. e.g. jdbc:oracle:thin:@ldap://oid:5000/mydb1,cn=OracleContext,dc=myco,dc=com
//
// Note that all JDK provided InitialContextFactory impls from the JDK registered via module descriptors
diff --git a/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/OracleReflections.java b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/OracleReflections.java
new file mode 100644
index 0000000000000..f489dc605beea
--- /dev/null
+++ b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/OracleReflections.java
@@ -0,0 +1,13 @@
+package io.quarkus.jdbc.oracle.runtime.graal;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+
+/**
+ * We don't use a build item here as we also need to register all the nested classes and there's no way to do it easily with the
+ * build item for now.
+ */
+@RegisterForReflection(targets = { oracle.jdbc.xa.OracleXADataSource.class,
+ oracle.jdbc.datasource.impl.OracleDataSource.class })
+public class OracleReflections {
+
+}
diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaDevServicesBuildTimeConfig.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaDevServicesBuildTimeConfig.java
index 756662efdd317..d3bbc818fc103 100644
--- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaDevServicesBuildTimeConfig.java
+++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaDevServicesBuildTimeConfig.java
@@ -4,6 +4,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -47,8 +48,8 @@ public class KafkaDevServicesBuildTimeConfig {
public Provider provider = Provider.REDPANDA;
public enum Provider {
- REDPANDA("docker.io/vectorized/redpanda:v22.3.4"),
- STRIMZI("quay.io/strimzi-test-container/test-container:latest-kafka-3.2.1"),
+ REDPANDA("docker.io/vectorized/redpanda:v24.1.2"),
+ STRIMZI("quay.io/strimzi-test-container/test-container:latest-kafka-3.7.0"),
KAFKA_NATIVE("quay.io/ogunalp/kafka-native:latest");
private final String defaultImageName;
@@ -106,6 +107,7 @@ public String getDefaultImageName() {
* The topic creation will not try to re-partition existing topics with different number of partitions.
*/
@ConfigItem
+ @ConfigDocMapKey("topic-name")
public Map topicPartitions;
/**
@@ -120,6 +122,7 @@ public String getDefaultImageName() {
* Environment variables that are passed to the container.
*/
@ConfigItem
+ @ConfigDocMapKey("environment-variable-name")
public Map containerEnv;
/**
diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java
index 393c85fdf3cd2..41f3415a761c6 100644
--- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java
+++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/KafkaProcessor.java
@@ -76,12 +76,12 @@
import io.quarkus.deployment.builditem.LogCategoryBuildItem;
import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem;
import io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassConditionBuildItem;
-import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
@@ -482,15 +482,16 @@ UnremovableBeanBuildItem ensureJsonParserAvailable() {
}
@BuildStep
- public void registerRuntimeInitializedClasses(BuildProducer producer) {
- // Classes using java.util.Random, which need to be runtime initialized
- producer.produce(
- new RuntimeInitializedClassBuildItem("org.apache.kafka.common.security.authenticator.SaslClientAuthenticator"));
- producer.produce(new RuntimeInitializedClassBuildItem(
- "org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin"));
- // VerificationKeyResolver is value on static map in OAuthBearerValidatorCallbackHandler
- producer.produce(new RuntimeInitializedClassBuildItem(
- "org.apache.kafka.common.security.oauthbearer.OAuthBearerValidatorCallbackHandler"));
+ NativeImageConfigBuildItem nativeImageConfiguration() {
+ NativeImageConfigBuildItem.Builder builder = NativeImageConfigBuildItem.builder()
+ // Classes using java.util.Random, which need to be runtime initialized
+ .addRuntimeInitializedClass("org.apache.kafka.common.security.authenticator.SaslClientAuthenticator")
+ .addRuntimeInitializedClass(
+ "org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin")
+ // VerificationKeyResolver is value on static map in OAuthBearerValidatorCallbackHandler
+ .addRuntimeInitializedClass("org.apache.kafka.common.security.oauthbearer.OAuthBearerValidatorCallbackHandler")
+ .addRuntimeReinitializedClass("org.apache.kafka.shaded.com.google.protobuf.UnsafeUtil");
+ return builder.build();
}
@BuildStep
diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java
index 504bc66168f4e..ba0f407ea5dec 100644
--- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java
+++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java
@@ -35,6 +35,9 @@ public Map createKafkaRuntimeConfig(Config config, ApplicationCo
if (!propertyNameLowerCase.startsWith(CONFIG_PREFIX) || propertyNameLowerCase.startsWith(UI_CONFIG_PREFIX)) {
continue;
}
+ if (propertyNameLowerCase.length() <= CONFIG_PREFIX.length()) {
+ continue;
+ }
// Replace _ by . - This is because Kafka properties tend to use . and env variables use _ for every special
// character. So, replace _ with .
String effectivePropertyName = propertyNameLowerCase.substring(CONFIG_PREFIX.length() + 1).toLowerCase()
diff --git a/extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/KafkaSubstitutions.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/KafkaSubstitutions.java
similarity index 51%
rename from extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/KafkaSubstitutions.java
rename to extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/KafkaSubstitutions.java
index 852c6ca247a6e..c93f9127eb827 100644
--- a/extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/KafkaSubstitutions.java
+++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/KafkaSubstitutions.java
@@ -1,10 +1,13 @@
-package io.smallrye.reactive.kafka.graal;
+package io.quarkus.kafka.client.runtime.graal;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
+import sun.misc.Unsafe;
+
@TargetClass(className = "org.apache.kafka.common.network.SaslChannelBuilder")
final class Target_org_apache_kafka_common_network_SaslChannelBuilder {
@@ -17,6 +20,20 @@ private static String defaultKerberosRealm() throws ClassNotFoundException, NoSu
}
+@TargetClass(className = "org.apache.kafka.shaded.com.google.protobuf.UnsafeUtil")
+final class Target_org_apache_kafka_shaded_com_google_protobuf_UnsafeUtil {
+ @Substitute
+ static sun.misc.Unsafe getUnsafe() {
+ try {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return (Unsafe) theUnsafe.get(null);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
+
class KafkaSubstitutions {
}
diff --git a/extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/StrimziSubstitutions.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/StrimziSubstitutions.java
similarity index 98%
rename from extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/StrimziSubstitutions.java
rename to extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/StrimziSubstitutions.java
index 2219060f6b7a8..d16d81ed9bf54 100644
--- a/extensions/kafka-client/runtime/src/main/java/io/smallrye/reactive/kafka/graal/StrimziSubstitutions.java
+++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/graal/StrimziSubstitutions.java
@@ -1,4 +1,4 @@
-package io.smallrye.reactive.kafka.graal;
+package io.quarkus.kafka.client.runtime.graal;
import java.util.function.BooleanSupplier;
diff --git a/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/KubernetesDevServicesBuildTimeConfig.java b/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/KubernetesDevServicesBuildTimeConfig.java
index d32eebd2ab80a..1c5dd077006dc 100644
--- a/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/KubernetesDevServicesBuildTimeConfig.java
+++ b/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/KubernetesDevServicesBuildTimeConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.smallrye.config.WithDefault;
public interface KubernetesDevServicesBuildTimeConfig {
@@ -67,6 +68,7 @@ public interface KubernetesDevServicesBuildTimeConfig {
/**
* Environment variables that are passed to the container.
*/
+ @ConfigDocMapKey("environment-variable-name")
Map containerEnv();
enum Flavor {
diff --git a/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/QuarkusHttpClientFactory.java b/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/QuarkusHttpClientFactory.java
index a54a030e5b170..ab8d71db34a07 100644
--- a/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/QuarkusHttpClientFactory.java
+++ b/extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/QuarkusHttpClientFactory.java
@@ -9,6 +9,7 @@
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.http.HttpClient;
import io.fabric8.kubernetes.client.vertx.VertxHttpClientBuilder;
+import io.quarkus.runtime.ResettableSystemProperties;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.file.FileSystemOptions;
@@ -35,21 +36,12 @@ private Vertx createVertxInstance() {
// This is done using the DISABLE_DNS_RESOLVER_PROP_NAME system property.
// The DNS resolver used by vert.x is configured during the (synchronous) initialization.
// So, we just need to disable the async resolver around the Vert.x instance creation.
- String originalValue = System.getProperty(DISABLE_DNS_RESOLVER_PROP_NAME);
- Vertx vertx;
- try {
- System.setProperty(DISABLE_DNS_RESOLVER_PROP_NAME, "true");
- vertx = Vertx.vertx(new VertxOptions().setFileSystemOptions(
+ try (var resettableSystemProperties = ResettableSystemProperties.of(
+ DISABLE_DNS_RESOLVER_PROP_NAME, "true")) {
+ return Vertx.vertx(new VertxOptions().setFileSystemOptions(
new FileSystemOptions().setFileCachingEnabled(false).setClassPathResolvingEnabled(false)));
- } finally {
- // Restore the original value
- if (originalValue == null) {
- System.clearProperty(DISABLE_DNS_RESOLVER_PROP_NAME);
- } else {
- System.setProperty(DISABLE_DNS_RESOLVER_PROP_NAME, originalValue);
- }
+
}
- return vertx;
}
@Override
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java
index de0eb2ad9f0f9..746d4ac101f6e 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleBindingConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -20,6 +21,7 @@ public class ClusterRoleBindingConfig {
* Labels to add into the RoleBinding resource.
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
public Map labels;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleConfig.java
index 7ac12a2e19f92..91d1fa838babf 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/ClusterRoleConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -19,6 +20,7 @@ public class ClusterRoleConfig {
* Labels to add into the ClusterRole resource.
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
Map labels;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java
index a8590aaa6e50c..a445f1fdc79e8 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/Constants.java
@@ -33,6 +33,8 @@ public final class Constants {
public static final String ROUTE = "Route";
public static final String ROUTE_API_GROUP = "route.openshift.io/v1";
+ static final String VERSION_LABEL = "app.kubernetes.io/version";
+
static final String OPENSHIFT_APP_RUNTIME = "app.openshift.io/runtime";
static final String S2I = "s2i";
static final String DEFAULT_S2I_IMAGE_NAME = "s2i-java"; //refers to the Dekorate default image.
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvVarsConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvVarsConfig.java
index cdcf6e947a93a..95ff8ea865e4e 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvVarsConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/EnvVarsConfig.java
@@ -4,6 +4,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -28,6 +29,7 @@ public class EnvVarsConfig {
* The map associating environment variable names to their associated field references they take their value from.
*/
@ConfigItem
+ @ConfigDocMapKey("environment-variable-name")
Map fields;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java
index 8cf029c7afc97..d1ea33b6c830a 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/IngressConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -38,6 +39,7 @@ public class IngressConfig {
* Custom annotations to add to exposition (route or ingress) resources
*/
@ConfigItem
+ @ConfigDocMapKey("annotation-name")
Map annotations;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java
index 4474a28da851e..5fc111bfe3b90 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeConfig.java
@@ -8,6 +8,7 @@
import io.dekorate.kubernetes.annotation.ImagePullPolicy;
import io.dekorate.kubernetes.annotation.ServiceType;
import io.quarkus.kubernetes.spi.DeployStrategy;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@@ -49,12 +50,14 @@ public class KnativeConfig implements PlatformConfiguration {
* Custom labels to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
Map labels;
/**
* Custom annotations to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("annotation-name")
Map annotations;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
index f2075c21cd51e..f83be2afcc74f 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesCommonHelper.java
@@ -1194,9 +1194,19 @@ private static List toPolicyRulesList(Map
}
private static String parseVCSUri(VCSUriConfig config, ScmInfo scm) {
- if (config.enabled) {
- return config.override.orElseGet(() -> scm != null ? Git.sanitizeRemoteUrl(scm.getRemote().get("origin")) : null);
+ if (!config.enabled) {
+ return null;
}
- return null;
+ if (config.override.isPresent()) {
+ return config.override.get();
+ }
+ if (scm == null) {
+ return null;
+ }
+ String originRemote = scm.getRemote().get("origin");
+ if (originRemote == null || originRemote.isBlank()) {
+ return null;
+ }
+ return Git.sanitizeRemoteUrl(originRemote);
}
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java
index f5aa8bf024442..33feeca5700e6 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesConfig.java
@@ -14,6 +14,7 @@
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.kubernetes.spi.DeployStrategy;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@@ -61,12 +62,14 @@ public class KubernetesConfig implements PlatformConfiguration {
* Custom labels to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
Map labels;
/**
* Custom annotations to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("annotation-name")
Map annotations;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
index 6c348b20180f1..4c1230552f255 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java
@@ -5,6 +5,7 @@
import static io.quarkus.kubernetes.deployment.Constants.KNATIVE;
import static io.quarkus.kubernetes.deployment.Constants.KUBERNETES;
import static io.quarkus.kubernetes.deployment.Constants.MINIKUBE;
+import static io.quarkus.kubernetes.deployment.Constants.VERSION_LABEL;
import java.io.File;
import java.io.FileInputStream;
@@ -17,17 +18,22 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import io.dekorate.utils.Serialization;
+import io.fabric8.kubernetes.api.builder.Visitor;
import io.fabric8.kubernetes.api.model.APIResourceList;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
+import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
+import io.fabric8.kubernetes.api.model.LabelSelectorFluent;
import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.batch.v1.Job;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
@@ -212,6 +218,11 @@ private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget,
throw new IllegalStateException(messsage);
}
+ list.getItems().stream().filter(distinctByResourceKey()).forEach(i -> {
+ Optional existing = Optional.ofNullable(client.resource(i).get());
+ checkLabelSelectorVersions(deploymentTarget, i, existing);
+ });
+
list.getItems().stream().filter(distinctByResourceKey()).forEach(i -> {
deployResource(deploymentTarget, client, i, optionalResourceDefinitions);
log.info("Applied: " + i.getKind() + " " + i.getMetadata().getName() + ".");
@@ -239,6 +250,7 @@ private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget,
private void deployResource(DeploymentTargetEntry deploymentTarget, KubernetesClient client, HasMetadata metadata,
List optionalResourceDefinitions) {
var r = findResource(client, metadata);
+ Optional existing = Optional.ofNullable(client.resource(metadata).get());
if (shouldDeleteExisting(deploymentTarget, metadata)) {
deleteResource(metadata, r);
}
@@ -385,7 +397,6 @@ private static boolean shouldDeleteExisting(DeploymentTargetEntry deploymentTarg
if (deploymentTarget.getDeployStrategy() != DeployStrategy.CreateOrUpdate) {
return false;
}
-
return KNATIVE.equalsIgnoreCase(deploymentTarget.getName())
|| resource instanceof Service
|| (Objects.equals("v1", resource.getApiVersion()) && Objects.equals("Service", resource.getKind()))
@@ -398,4 +409,44 @@ private static Predicate distinctByResourceKey() {
return t -> seen.putIfAbsent(t.getApiVersion() + "/" + t.getKind() + ":" + t.getMetadata().getName(),
Boolean.TRUE) == null;
}
+
+ private static void checkLabelSelectorVersions(DeploymentTargetEntry deploymnetTarget, HasMetadata resource,
+ Optional existing) {
+ if (!existing.isPresent()) {
+ return;
+ }
+
+ if (resource instanceof Deployment) {
+ Optional version = getLabelSelectorVersion(resource);
+ Optional existingVersion = getLabelSelectorVersion(existing.get());
+ if (version.isPresent() && existingVersion.isPresent()) {
+ if (!version.get().equals(existingVersion.get())) {
+ throw new IllegalStateException(String.format(
+ "A previous Deployment with a conflicting label %s=%s was found in the label selector (current is %s=%s). As the label selector is immutable, you need to either align versions or manually delete previous deployment.",
+ VERSION_LABEL, existingVersion.get(), VERSION_LABEL, version.get()));
+ }
+ } else if (version.isPresent()) {
+ throw new IllegalStateException(String.format(
+ "A Deployment with a conflicting label %s=%s was in the label selector was requested (previous had no such label). As the label selector is immutable, you need to either manually delete previous deployment, or remove the label (consider using quarkus.%s.add-version-to-label-selectors=false).",
+ VERSION_LABEL, version.get(), deploymnetTarget.getName().toLowerCase()));
+ } else if (existingVersion.isPresent()) {
+ throw new IllegalStateException(String.format(
+ "A Deployment with no label in the label selector was requested (previous includes %s=%s). As the label selector is immutable, you need to either manually delete previous deployment, or ensure the %s label is present (consider using quarkus.%s.add-version-to-label-selectors=true).",
+ VERSION_LABEL, existingVersion.get(), VERSION_LABEL, deploymnetTarget.getName().toLowerCase()));
+ }
+ }
+ }
+
+ private static Optional getLabelSelectorVersion(HasMetadata resource) {
+ AtomicReference version = new AtomicReference<>();
+ KubernetesList list = new KubernetesListBuilder().addToItems(resource).accept(new Visitor>() {
+ @Override
+ public void visit(LabelSelectorFluent> item) {
+ if (item.getMatchLabels() != null) {
+ version.set(item.getMatchLabels().get(VERSION_LABEL));
+ }
+ }
+ }).build();
+ return Optional.ofNullable(version.get());
+ }
}
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java
index 89c6fb56ddc1f..1ac35b16e3b0d 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftConfig.java
@@ -17,6 +17,7 @@
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.kubernetes.spi.DeployStrategy;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
@@ -80,12 +81,14 @@ public static enum OpenshiftFlavor {
* Custom labels to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
Map labels;
/**
* Custom annotations to add to all resources
*/
@ConfigItem
+ @ConfigDocMapKey("annotation-name")
Map annotations;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleBindingConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleBindingConfig.java
index e390ea2d649e9..5acd683c01477 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleBindingConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleBindingConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -20,6 +21,7 @@ public class RoleBindingConfig {
* Labels to add into the RoleBinding resource.
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
public Map labels;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleConfig.java
index 5edb212b6a816..7717555cfcea9 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RoleConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -25,6 +26,7 @@ public class RoleConfig {
* Labels to add into the Role resource.
*/
@ConfigItem
+ @ConfigDocMapKey("label-name")
Map labels;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RouteConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RouteConfig.java
index 4e175e29724ca..dd8b2f4de974f 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RouteConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/RouteConfig.java
@@ -3,6 +3,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -32,6 +33,7 @@ public class RouteConfig {
* Custom annotations to add to exposition (route or ingress) resources
*/
@ConfigItem
+ @ConfigDocMapKey("annotation-name")
Map annotations;
/**
diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecurityContextConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecurityContextConfig.java
index a91e0a8014c2f..038f8b84d7a4e 100644
--- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecurityContextConfig.java
+++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/SecurityContextConfig.java
@@ -4,6 +4,7 @@
import java.util.Map;
import java.util.Optional;
+import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
@@ -55,6 +56,7 @@ public class SecurityContextConfig {
* Sysctls hold a list of namespaced sysctls used for the pod.
*/
@ConfigItem
+ @ConfigDocMapKey("sysctl-name")
Optional