From e3fcd09405088c238eaac16a608fcba15f4b3407 Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Tue, 27 Jan 2026 14:53:36 -0500 Subject: [PATCH 1/5] Introduce RealmConfigurationSource The existing `PolarisConfigurationStore` interface is a fixture of SPI methods providing config values from the environment and utility methods for various lookup parameter permutations and type casts. This change adds `RealmConfigurationSource` for the SPI part and moves lookup logic into `RealmConfigImpl`. Note that existing service code always accesses config via `RealmConfig`. The old `PolarisConfigurationStore` interface remain for backward compatibility and but redirects actual config lookup to `RealmConfigImpl`. --- .../maintenance/TestCatalogMaintenance.java | 8 +- .../metastore/TestNoSqlMetaStoreManager.java | 6 +- .../nosql/metastore/TestNoSqlResolver.java | 6 +- .../nosql/metastore/CdiProducers.java | 6 +- .../polaris/core/PolarisCallContext.java | 23 ++- .../config/PolarisConfigurationStore.java | 63 ++----- .../polaris/core/config/RealmConfig.java | 4 +- .../polaris/core/config/RealmConfigImpl.java | 78 ++++++++- .../core/config/RealmConfigurationSource.java | 50 ++++++ .../core/config/RealmConfigImplTest.java | 159 ++++++++++++++++++ .../storage/BaseStorageIntegrationTest.java | 4 +- .../InMemoryStorageIntegrationTest.java | 31 +--- .../cache/StorageCredentialCacheTest.java | 23 +-- .../AwsCredentialsStorageIntegrationTest.java | 123 +++----------- .../admintool/config/AdminToolProducers.java | 12 +- .../config/DefaultConfigurationStore.java | 19 ++- .../service/config/ServiceProducers.java | 10 +- ...bstractIcebergCatalogHandlerAuthzTest.java | 6 +- .../service/entity/CatalogEntityTest.java | 4 +- .../test/PolarisIntegrationTestFixture.java | 2 +- .../test/PolarisIntegrationTestHelper.java | 4 +- .../apache/polaris/service/TestServices.java | 29 +--- 22 files changed, 401 insertions(+), 269 deletions(-) create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java create mode 100644 polaris-core/src/test/java/org/apache/polaris/core/config/RealmConfigImplTest.java diff --git a/persistence/nosql/persistence/metastore-maintenance/src/test/java/org/apache/polaris/persistence/nosql/metastore/maintenance/TestCatalogMaintenance.java b/persistence/nosql/persistence/metastore-maintenance/src/test/java/org/apache/polaris/persistence/nosql/metastore/maintenance/TestCatalogMaintenance.java index 84ffa51a8c..91f018bd1f 100644 --- a/persistence/nosql/persistence/metastore-maintenance/src/test/java/org/apache/polaris/persistence/nosql/metastore/maintenance/TestCatalogMaintenance.java +++ b/persistence/nosql/persistence/metastore-maintenance/src/test/java/org/apache/polaris/persistence/nosql/metastore/maintenance/TestCatalogMaintenance.java @@ -53,7 +53,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; @@ -101,7 +101,7 @@ public class TestCatalogMaintenance { @Inject MaintenanceService maintenance; @Inject MutableMonotonicClock mutableMonotonicClock; - @Inject PolarisConfigurationStore configurationStore; + @Inject RealmConfigurationSource configurationSource; @Inject CacheBackend cacheBackend; @Inject RealmPersistenceFactory realmPersistenceFactory; @@ -139,7 +139,7 @@ public void catalogMaintenance() { var manager = metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); var session = metaStoreManagerFactory.getOrCreateSession(realmContext); - var callCtx = new PolarisCallContext(realmContext, session, configurationStore); + var callCtx = new PolarisCallContext(realmContext, session, configurationSource); var persistence = realmPersistenceFactory.newBuilder().realmId(realmId).skipDecorators().build(); @@ -455,7 +455,7 @@ private void checkEntities(String step, List entities) { var manager = metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); var session = metaStoreManagerFactory.getOrCreateSession(realmContext); - var callCtx = new PolarisCallContext(realmContext, session, configurationStore); + var callCtx = new PolarisCallContext(realmContext, session, configurationSource); for (var e : entities) { var result = diff --git a/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlMetaStoreManager.java b/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlMetaStoreManager.java index e6a15e96ab..1c41980dbb 100644 --- a/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlMetaStoreManager.java +++ b/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlMetaStoreManager.java @@ -31,7 +31,7 @@ import java.util.UUID; import org.apache.iceberg.catalog.Namespace; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.NamespaceEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; @@ -70,7 +70,7 @@ public class TestNoSqlMetaStoreManager extends BasePolarisMetaStoreManagerTest { @Identifier("nosql") MetaStoreManagerFactory metaStoreManagerFactory; - @Inject PolarisConfigurationStore configurationStore; + @Inject RealmConfigurationSource configurationSource; @Inject MonotonicClock monotonicClock; String realmId; @@ -91,7 +91,7 @@ protected PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() { var manager = metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); var session = metaStoreManagerFactory.getOrCreateSession(realmContext); - var callCtx = new PolarisCallContext(realmContext, session, configurationStore); + var callCtx = new PolarisCallContext(realmContext, session, configurationSource); return new PolarisTestMetaStoreManager(manager, callCtx, startTime, false); } diff --git a/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlResolver.java b/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlResolver.java index 464af9ddbd..0488bfda2f 100644 --- a/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlResolver.java +++ b/persistence/nosql/persistence/metastore/src/test/java/org/apache/polaris/persistence/nosql/metastore/TestNoSqlResolver.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.UUID; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisGrantRecord; import org.apache.polaris.core.persistence.BaseResolverTest; @@ -48,7 +48,7 @@ public class TestNoSqlResolver extends BaseResolverTest { @Identifier("nosql") MetaStoreManagerFactory metaStoreManagerFactory; - @Inject PolarisConfigurationStore configurationStore; + @Inject RealmConfigurationSource configurationSource; @Inject MonotonicClock monotonicClock; PolarisMetaStoreManager metaStoreManager; @@ -73,7 +73,7 @@ protected PolarisCallContext callCtx() { metaStoreManager = metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); var session = metaStoreManagerFactory.getOrCreateSession(realmContext); - callCtx = new PolarisCallContext(realmContext, session, configurationStore); + callCtx = new PolarisCallContext(realmContext, session, configurationSource); tm = new PolarisTestMetaStoreManager(metaStoreManager, callCtx, startTime, false); } diff --git a/persistence/nosql/persistence/metastore/src/testFixtures/java/org/apache/polaris/persistence/nosql/metastore/CdiProducers.java b/persistence/nosql/persistence/metastore/src/testFixtures/java/org/apache/polaris/persistence/nosql/metastore/CdiProducers.java index 79de0d6ae1..126ce395f1 100644 --- a/persistence/nosql/persistence/metastore/src/testFixtures/java/org/apache/polaris/persistence/nosql/metastore/CdiProducers.java +++ b/persistence/nosql/persistence/metastore/src/testFixtures/java/org/apache/polaris/persistence/nosql/metastore/CdiProducers.java @@ -24,7 +24,7 @@ import java.time.Clock; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.PolarisStorageIntegration; import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider; @@ -44,8 +44,8 @@ PolarisStorageIntegration getStorageIntegrationForConfig( } @Produces - PolarisConfigurationStore producePolarisConfigurationStore() { - return new PolarisConfigurationStore() {}; + RealmConfigurationSource produceRealmConfigurationSource() { + return RealmConfigurationSource.EMPTY_CONFIG; } @Produces diff --git a/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java b/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java index 272b455e76..590ed28abd 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java @@ -22,6 +22,7 @@ import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.BasePersistence; @@ -34,23 +35,35 @@ public class PolarisCallContext implements CallContext { // meta store which is used to persist Polaris entity metadata private final BasePersistence metaStore; - private final PolarisConfigurationStore configurationStore; + private final RealmConfigurationSource configurationSource; private final RealmContext realmContext; private final RealmConfig realmConfig; + /** + * @deprecated Use {@link PolarisCallContext#PolarisCallContext(RealmContext, BasePersistence, + * RealmConfigurationSource)}. + */ + @Deprecated(forRemoval = true) public PolarisCallContext( @Nonnull RealmContext realmContext, @Nonnull BasePersistence metaStore, @Nonnull PolarisConfigurationStore configurationStore) { + this(realmContext, metaStore, configurationStore::getConfiguration); + } + + public PolarisCallContext( + @Nonnull RealmContext realmContext, + @Nonnull BasePersistence metaStore, + @Nonnull RealmConfigurationSource configurationSource) { this.realmContext = realmContext; this.metaStore = metaStore; - this.configurationStore = configurationStore; - this.realmConfig = new RealmConfigImpl(this.configurationStore, this.realmContext); + this.configurationSource = configurationSource; + this.realmConfig = new RealmConfigImpl(this.configurationSource, this.realmContext); } public PolarisCallContext( @Nonnull RealmContext realmContext, @Nonnull BasePersistence metaStore) { - this(realmContext, metaStore, new PolarisConfigurationStore() {}); + this(realmContext, metaStore, RealmConfigurationSource.EMPTY_CONFIG); } public BasePersistence getMetaStore() { @@ -81,6 +94,6 @@ public PolarisCallContext copy() { // copy of the RealmContext to ensure the access during the task executor. String realmId = this.realmContext.getRealmIdentifier(); RealmContext realmContext = () -> realmId; - return new PolarisCallContext(realmContext, this.metaStore, this.configurationStore); + return new PolarisCallContext(realmContext, this.metaStore, this.configurationSource); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java b/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java index 21bae33084..03bbe72c08 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java @@ -18,11 +18,8 @@ */ package org.apache.polaris.core.config; -import com.google.common.base.Preconditions; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; @@ -57,36 +54,13 @@ public interface PolarisConfigurationStore { * @param defaultValue the default value if the configuration key has no value * @return the current value or the supplied default value * @param the type of the configuration value + * @deprecated Use {@link RealmConfig}. */ + @SuppressWarnings({"DeprecatedIsStillUsed", "removal"}) + @Deprecated(forRemoval = true) default @Nonnull T getConfiguration( @Nonnull RealmContext realmContext, String configName, @Nonnull T defaultValue) { - Preconditions.checkNotNull(defaultValue, "Cannot pass null as a default value"); - T configValue = getConfiguration(realmContext, configName); - return configValue != null ? configValue : defaultValue; - } - - /** - * In some cases, we may extract a value that doesn't match the expected type for a config. This - * method can be used to attempt to force-cast it using `String.valueOf` - */ - private @Nonnull T tryCast(PolarisConfiguration config, Object value) { - if (value == null) { - return config.defaultValue(); - } - - if (config.defaultValue() instanceof Boolean) { - return config.cast(Boolean.valueOf(String.valueOf(value))); - } else if (config.defaultValue() instanceof Integer) { - return config.cast(Integer.valueOf(String.valueOf(value))); - } else if (config.defaultValue() instanceof Long) { - return config.cast(Long.valueOf(String.valueOf(value))); - } else if (config.defaultValue() instanceof Double) { - return config.cast(Double.valueOf(String.valueOf(value))); - } else if (config.defaultValue() instanceof List) { - return config.cast(new ArrayList<>((List) value)); - } else { - return config.cast(value); - } + return asRealmConfig(realmContext).getConfig(configName, defaultValue); } /** @@ -99,8 +73,7 @@ public interface PolarisConfigurationStore { */ default @Nonnull T getConfiguration( @Nonnull RealmContext realmContext, PolarisConfiguration config) { - T result = getConfiguration(realmContext, config.key(), config.defaultValue()); - return tryCast(config, result); + return asRealmConfig(realmContext).getConfig(config); } /** @@ -138,26 +111,10 @@ public interface PolarisConfigurationStore { @Nonnull RealmContext realmContext, @Nonnull Map catalogProperties, PolarisConfiguration config) { - if (config.hasCatalogConfig() || config.hasCatalogConfigUnsafe()) { - String propertyValue = null; - if (config.hasCatalogConfig()) { - propertyValue = catalogProperties.get(config.catalogConfig()); - } - if (propertyValue == null) { - if (config.hasCatalogConfigUnsafe()) { - propertyValue = catalogProperties.get(config.catalogConfigUnsafe()); - } - if (propertyValue != null) { - LOGGER.warn( - String.format( - "Deprecated config %s is in use and will be removed in a future version", - config.catalogConfigUnsafe())); - } - } - if (propertyValue != null) { - return tryCast(config, propertyValue); - } - } - return getConfiguration(realmContext, config); + return asRealmConfig(realmContext).getConfig(config, catalogProperties); + } + + private RealmConfig asRealmConfig(RealmContext realmContext) { + return new RealmConfigImpl(this::getConfiguration, realmContext); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfig.java b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfig.java index 62135d72dd..5d0096d6ae 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfig.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfig.java @@ -33,7 +33,7 @@ public interface RealmConfig { * @return the current value set for the configuration key, or null if not set * @deprecated Use typed {@link #getConfig(PolarisConfiguration)} instead. */ - @Deprecated + @Deprecated(forRemoval = true) @Nullable T getConfig(String configName); /** @@ -44,7 +44,9 @@ public interface RealmConfig { * @param configName the name of the configuration key to check * @param defaultValue the default value if the configuration key has no value * @return the current value or the supplied default value + * @deprecated Use typed {@link #getConfig(PolarisConfiguration)} instead. */ + @Deprecated(forRemoval = true) T getConfig(String configName, T defaultValue); /** diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigImpl.java b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigImpl.java index d2c28bae2f..7516256585 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigImpl.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigImpl.java @@ -18,43 +18,105 @@ */ package org.apache.polaris.core.config; +import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RealmConfigImpl implements RealmConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(RealmConfigImpl.class); - private final PolarisConfigurationStore configurationStore; + private final RealmConfigurationSource configurationSource; private final RealmContext realmContext; - public RealmConfigImpl(PolarisConfigurationStore configurationStore, RealmContext realmContext) { - this.configurationStore = configurationStore; + public RealmConfigImpl(RealmConfigurationSource configurationSource, RealmContext realmContext) { + this.configurationSource = configurationSource; this.realmContext = realmContext; } + @SuppressWarnings("removal") @Override public @Nullable T getConfig(String configName) { - return configurationStore.getConfiguration(realmContext, configName); + @SuppressWarnings("unchecked") + T value = (T) configurationSource.getConfigValue(realmContext, configName); + return value; } + @SuppressWarnings("removal") @Override public T getConfig(String configName, T defaultValue) { - return configurationStore.getConfiguration(realmContext, configName, defaultValue); + @SuppressWarnings("unchecked") + T value = (T) getConfig(configName); + if (value == null) { + return defaultValue; + } + return value; } @Override public T getConfig(PolarisConfiguration config) { - return configurationStore.getConfiguration(realmContext, config); + return getConfig(config, Collections.emptyMap()); } @Override public T getConfig(PolarisConfiguration config, CatalogEntity catalogEntity) { - return configurationStore.getConfiguration(realmContext, catalogEntity, config); + return getConfig(config, catalogEntity.getPropertiesAsMap()); } @Override public T getConfig(PolarisConfiguration config, Map catalogProperties) { - return configurationStore.getConfiguration(realmContext, catalogProperties, config); + Object propertyValue = null; + if (config.hasCatalogConfig() || config.hasCatalogConfigUnsafe()) { + if (config.hasCatalogConfig()) { + propertyValue = catalogProperties.get(config.catalogConfig()); + } + if (propertyValue == null) { + if (config.hasCatalogConfigUnsafe()) { + propertyValue = catalogProperties.get(config.catalogConfigUnsafe()); + } + if (propertyValue != null) { + LOGGER.warn( + String.format( + "Deprecated config %s is in use and will be removed in a future version", + config.catalogConfigUnsafe())); + } + } + } + + if (propertyValue == null) { + propertyValue = configurationSource.getConfigValue(realmContext, config.key()); + } + + return tryCast(config, propertyValue); + } + + /** + * In some cases, we may extract a value that doesn't match the expected type for a config. This + * method can be used to attempt to force-cast it using `String.valueOf` + */ + private @Nonnull T tryCast(PolarisConfiguration config, Object value) { + if (value == null) { + return config.defaultValue(); + } + + if (config.defaultValue() instanceof Boolean) { + return config.cast(Boolean.valueOf(String.valueOf(value))); + } else if (config.defaultValue() instanceof Integer) { + return config.cast(Integer.valueOf(String.valueOf(value))); + } else if (config.defaultValue() instanceof Long) { + return config.cast(Long.valueOf(String.valueOf(value))); + } else if (config.defaultValue() instanceof Double) { + return config.cast(Double.valueOf(String.valueOf(value))); + } else if (config.defaultValue() instanceof List) { + return config.cast(new ArrayList<>((List) value)); + } else { + return config.cast(value); + } } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java new file mode 100644 index 0000000000..e49ce4af7b --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.polaris.core.config; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.util.Map; +import org.apache.polaris.core.context.RealmContext; + +/** + * This is an SPI interface used by Polaris Core for loading base configuration values from the + * environment. + * + *

Core classes are expected to use {@link RealmConfig} for configuration lookup in runtime. + */ +public interface RealmConfigurationSource { + RealmConfigurationSource EMPTY_CONFIG = (rc, name) -> null; + + static RealmConfigurationSource global(Map config) { + return (rc, name) -> config.get(name); + } + + /** + * Retrieve the current value for a configuration key for a given realm. May be null if not set. + * + * @param realmContext realm context for the configuration lookup request. + * @param configName the name of the configuration key to look up. + * @return the current value set for the configuration key for the given realm, or null if not + * set. + */ + @Nullable + Object getConfigValue(@Nonnull RealmContext realmContext, String configName); +} diff --git a/polaris-core/src/test/java/org/apache/polaris/core/config/RealmConfigImplTest.java b/polaris-core/src/test/java/org/apache/polaris/core/config/RealmConfigImplTest.java new file mode 100644 index 0000000000..1cd05541b5 --- /dev/null +++ b/polaris-core/src/test/java/org/apache/polaris/core/config/RealmConfigImplTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.polaris.core.config; + +import static org.apache.polaris.core.config.RealmConfigurationSource.EMPTY_CONFIG; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.apache.polaris.core.entity.CatalogEntity; +import org.junit.jupiter.api.Test; + +class RealmConfigImplTest { + + private static final RealmConfigurationSource stringsSource = + (rc, name) -> String.format("value-%s-%s", rc.getRealmIdentifier(), name); + + private RealmConfig strings(String realmName) { + return new RealmConfigImpl(stringsSource, () -> realmName); + } + + private RealmConfig fixed(Object value) { + return new RealmConfigImpl((rc, name) -> value, () -> "test-realm"); + } + + private RealmConfig empty() { + return new RealmConfigImpl(EMPTY_CONFIG, () -> "test-realm"); + } + + @Test + @SuppressWarnings("removal") + void legacyLookup() { + assertThat((Object) strings("rc1").getConfig("c1")).isEqualTo("value-rc1-c1"); + assertThat((Object) strings("rc2").getConfig("c1", "default1")).isEqualTo("value-rc2-c1"); + assertThat((Object) empty().getConfig("c1", "default1")).isEqualTo("default1"); + } + + @Test + void configPropertyOrder() { + @SuppressWarnings("deprecation") + FeatureConfiguration cfg1 = + PolarisConfiguration.builder() + .key("TEST-KEY1") + .catalogConfig("polaris.config.cat-prop") + .catalogConfigUnsafe("legacy-prop") + .description("test") + .defaultValue("default2") + .buildFeatureConfiguration(); + assertThat(strings("rc1").getConfig(cfg1)).isEqualTo("value-rc1-TEST-KEY1"); + assertThat(strings("rc2").getConfig(cfg1, Map.of())).isEqualTo("value-rc2-TEST-KEY1"); + assertThat(strings("rc3").getConfig(cfg1, Map.of("polaris.config.cat-prop", "cat1"))) + .isEqualTo("cat1"); + assertThat(strings("rc3").getConfig(cfg1, Map.of("legacy-prop", "old2"))).isEqualTo("old2"); + assertThat(empty().getConfig(cfg1)).isEqualTo("default2"); + + @SuppressWarnings("deprecation") + FeatureConfiguration cfg2 = + PolarisConfiguration.builder() + .key("TEST-KEY2") + .catalogConfigUnsafe("legacy-prop2") + .description("test") + .defaultValue("default2") + .buildFeatureConfiguration(); + assertThat(strings("rc1").getConfig(cfg2)).isEqualTo("value-rc1-TEST-KEY2"); + assertThat(strings("rc2").getConfig(cfg2, Map.of())).isEqualTo("value-rc2-TEST-KEY2"); + assertThat(strings("rc3").getConfig(cfg2, Map.of("legacy-prop2", "old2"))).isEqualTo("old2"); + assertThat(empty().getConfig(cfg2)).isEqualTo("default2"); + + FeatureConfiguration cfg3 = + PolarisConfiguration.builder() + .key("TEST-KEY3") + .catalogConfig("polaris.config.cat-prop2") + .description("test") + .defaultValue("default2") + .buildFeatureConfiguration(); + assertThat(strings("rc1").getConfig(cfg3)).isEqualTo("value-rc1-TEST-KEY3"); + assertThat(strings("rc2").getConfig(cfg3, Map.of())).isEqualTo("value-rc2-TEST-KEY3"); + assertThat(strings("rc3").getConfig(cfg3, Map.of("polaris.config.cat-prop2", "cat2"))) + .isEqualTo("cat2"); + assertThat(empty().getConfig(cfg3)).isEqualTo("default2"); + + FeatureConfiguration cfg4 = + PolarisConfiguration.builder() + .key("TEST-KEY4") + .description("test") + .defaultValue("default2") + .buildFeatureConfiguration(); + assertThat(strings("rc1").getConfig(cfg4)).isEqualTo("value-rc1-TEST-KEY4"); + assertThat(strings("rc2").getConfig(cfg4, Map.of())).isEqualTo("value-rc2-TEST-KEY4"); + assertThat(empty().getConfig(cfg4)).isEqualTo("default2"); + } + + @Test + void entityProperties() { + FeatureConfiguration cfg = + PolarisConfiguration.builder() + .key("TEST-ENTITY1") + .catalogConfig("polaris.config.test-entity-prop") + .description("test") + .defaultValue("default2") + .buildFeatureConfiguration(); + + CatalogEntity entity = + new CatalogEntity.Builder() + .setProperties(Map.of("polaris.config.test-entity-prop", "entity2")) + .build(); + + assertThat(strings("rc1").getConfig(cfg)).isEqualTo("value-rc1-TEST-ENTITY1"); + assertThat(strings("rc2").getConfig(cfg, entity)).isEqualTo("entity2"); + } + + private PolarisConfiguration cfg(T value) { + return PolarisConfiguration.builder() + .key("TEST-CAST-" + UUID.randomUUID().toString()) + .description("test") + .defaultValue(value) + .buildFeatureConfiguration(); + } + + @Test + void typeCast() { + assertThat(fixed("str").getConfig(cfg("test"))).isEqualTo("str"); + assertThat(fixed(1).getConfig(cfg(2))).isEqualTo(1); + assertThat(fixed(2L).getConfig(cfg(1L))).isEqualTo(2L); + assertThat(fixed(true).getConfig(cfg(false))).isEqualTo(true); + assertThat(fixed(1.2f).getConfig(cfg(0.0f))).isEqualTo(1.2f); + assertThat(fixed(3.4d).getConfig(cfg(0.1d))).isEqualTo(3.4d); + assertThat(fixed(List.of("1", "2")).getConfig(cfg(List.of()))).isEqualTo(List.of("1", "2")); + } + + @Test + void typedDefaults() { + assertThat(empty().getConfig(cfg("test"))).isEqualTo("test"); + assertThat(empty().getConfig(cfg(2))).isEqualTo(2); + assertThat(empty().getConfig(cfg(1L))).isEqualTo(1L); + assertThat(empty().getConfig(cfg(false))).isEqualTo(false); + assertThat(empty().getConfig(cfg(0.1f))).isEqualTo(0.1f); + assertThat(empty().getConfig(cfg(2.3d))).isEqualTo(2.3d); + assertThat(empty().getConfig(cfg(List.of("3", "4")))).isEqualTo(List.of("3", "4")); + } +} diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/BaseStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/BaseStorageIntegrationTest.java index 6a93002295..adca38632a 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/BaseStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/BaseStorageIntegrationTest.java @@ -19,11 +19,11 @@ package org.apache.polaris.core.storage; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.config.RealmConfigurationSource; public abstract class BaseStorageIntegrationTest { protected static final RealmConfigImpl EMPTY_REALM_CONFIG = - new RealmConfigImpl(new PolarisConfigurationStore() {}, () -> "realm"); + new RealmConfigImpl(RealmConfigurationSource.EMPTY_CONFIG, () -> "realm"); } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java index 582af20db5..bd18ccdf13 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java @@ -18,13 +18,13 @@ */ package org.apache.polaris.core.storage; +import static org.apache.polaris.core.config.RealmConfigurationSource.EMPTY_CONFIG; + import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.util.Map; import java.util.Optional; import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.context.RealmContext; @@ -43,8 +43,7 @@ class InMemoryStorageIntegrationTest { @ParameterizedTest @CsvSource({"s3,s3", "s3,s3a", "s3a,s3", "s3a,s3a"}) public void testValidateAccessToLocations(String allowedScheme, String locationScheme) { - RealmConfig realmConfig = - new RealmConfigImpl(new MockedConfigurationStore(Map.of()), REALM_CONTEXT); + RealmConfig realmConfig = new RealmConfigImpl(EMPTY_CONFIG, REALM_CONTEXT); MockInMemoryStorageIntegration storage = new MockInMemoryStorageIntegration(); Map> result = storage.validateAccessToLocations( @@ -86,8 +85,7 @@ public void testValidateAccessToLocations(String allowedScheme, String locationS public void testValidateAccessToLocationsWithWildcard(String s3Scheme) { MockInMemoryStorageIntegration storage = new MockInMemoryStorageIntegration(); Map config = Map.of("ALLOW_WILDCARD_LOCATION", true); - RealmConfig realmConfig = - new RealmConfigImpl(new MockedConfigurationStore(config), REALM_CONTEXT); + RealmConfig realmConfig = new RealmConfigImpl((rc, name) -> config.get(name), REALM_CONTEXT); Map> result = storage.validateAccessToLocations( realmConfig, @@ -128,8 +126,7 @@ public void testValidateAccessToLocationsWithWildcard(String s3Scheme) { @Test public void testValidateAccessToLocationsNoAllowedLocations() { MockInMemoryStorageIntegration storage = new MockInMemoryStorageIntegration(); - RealmConfig realmConfig = - new RealmConfigImpl(new MockedConfigurationStore(Map.of()), REALM_CONTEXT); + RealmConfig realmConfig = new RealmConfigImpl(EMPTY_CONFIG, REALM_CONTEXT); Map> result = storage.validateAccessToLocations( realmConfig, @@ -164,8 +161,7 @@ public void testValidateAccessToLocationsNoAllowedLocations() { @Test public void testValidateAccessToLocationsWithPrefixOfAllowedLocation() { MockInMemoryStorageIntegration storage = new MockInMemoryStorageIntegration(); - RealmConfig realmConfig = - new RealmConfigImpl(new MockedConfigurationStore(Map.of()), REALM_CONTEXT); + RealmConfig realmConfig = new RealmConfigImpl(EMPTY_CONFIG, REALM_CONTEXT); Map> result = storage.validateAccessToLocations( realmConfig, @@ -206,19 +202,4 @@ public StorageAccessConfig getSubscopedCreds( return null; } } - - private static class MockedConfigurationStore implements PolarisConfigurationStore { - private final Map defaults; - - public MockedConfigurationStore(Map defaults) { - this.defaults = Map.copyOf(defaults); - } - - @Override - public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { - @SuppressWarnings("unchecked") - T confgValue = (T) defaults.get(configName); - return confgValue; - } - } } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java index 98464dc30d..7a13e3c582 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java @@ -18,6 +18,8 @@ */ package org.apache.polaris.core.storage.cache; +import static org.apache.polaris.core.config.RealmConfigurationSource.EMPTY_CONFIG; + import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; @@ -30,9 +32,9 @@ import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -54,8 +56,7 @@ public class StorageCredentialCacheTest { private final PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl(); private final RealmContext realmContext = () -> "testRealm"; - private final RealmConfig realmConfig = - new RealmConfigImpl(new PolarisConfigurationStore() {}, realmContext); + private final RealmConfig realmConfig = new RealmConfigImpl(EMPTY_CONFIG, realmContext); private final StorageCredentialsVendor storageCredentialsVendor; private StorageCredentialCache storageCredentialCache; @@ -225,18 +226,10 @@ public void testCacheMissForAnotherPrincipal() { Mockito.when(storageCredentialsVendor.getRealmConfig()) .thenReturn( new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL - .key())) { - return "true"; - } - return null; - } - }, + RealmConfigurationSource.global( + Map.of( + FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), + "true")), () -> "realm")); testCacheForAnotherPrincipal(false); diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java index fdea3b0b5a..e9124d505f 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -28,10 +28,9 @@ import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; -import org.apache.polaris.core.context.RealmContext; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.storage.BaseStorageIntegrationTest; import org.apache.polaris.core.storage.CredentialVendingContext; import org.apache.polaris.core.storage.StorageAccessConfig; @@ -65,17 +64,17 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { public static final RealmConfig PRINCIPAL_INCLUDER_REALM_CONFIG = new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, + RealmConfigurationSource.global( + Map.of( + FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), + "true")), + () -> "realm"); + + private static final RealmConfigImpl SESSION_TAGS_ENABLED_CONFIG = + new RealmConfigImpl( + RealmConfigurationSource.global( + Map.of( + FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true")), () -> "realm"); public static final AssumeRoleResponse ASSUME_ROLE_RESPONSE = @@ -1146,20 +1145,7 @@ public void testSessionTagsIncludedWhenFeatureEnabled() { String warehouseKeyPrefix = "path/to/warehouse"; // Create a realm config with session tags enabled - RealmConfig sessionTagsEnabledConfig = - new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, - () -> "realm"); + RealmConfig sessionTagsEnabledConfig = SESSION_TAGS_ENABLED_CONFIG; ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); @@ -1232,21 +1218,10 @@ public void testSessionTagsWithTraceIdWhenBothFlagsEnabled() { // Create a realm config with both session tags AND trace_id enabled RealmConfig sessionTagsAndTraceIdEnabledConfig = new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - if (configName.equals( - FeatureConfiguration.INCLUDE_TRACE_ID_IN_SESSION_TAGS.key())) { - return "true"; - } - return null; - } - }, + RealmConfigurationSource.global( + Map.of( + FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true", + FeatureConfiguration.INCLUDE_TRACE_ID_IN_SESSION_TAGS.key(), "true")), () -> "realm"); ArgumentCaptor requestCaptor = @@ -1361,20 +1336,7 @@ public void testSessionTagsWithPartialContext() { String bucket = "bucket"; String warehouseKeyPrefix = "path/to/warehouse"; - RealmConfig sessionTagsEnabledConfig = - new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, - () -> "realm"); + RealmConfig sessionTagsEnabledConfig = SESSION_TAGS_ENABLED_CONFIG; ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); @@ -1428,21 +1390,6 @@ public void testSessionTagsWithLongValues() { String bucket = "bucket"; String warehouseKeyPrefix = "path/to/warehouse"; - RealmConfig sessionTagsEnabledConfig = - new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, - () -> "realm"); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); Mockito.when(stsClient.assumeRole(requestCaptor.capture())).thenReturn(ASSUME_ROLE_RESPONSE); @@ -1463,7 +1410,7 @@ public String getConfiguration(@Nonnull RealmContext ctx, String configName) { .build(), stsClient) .getSubscopedCreds( - sessionTagsEnabledConfig, + SESSION_TAGS_ENABLED_CONFIG, true, Set.of(s3Path(bucket, warehouseKeyPrefix)), Set.of(s3Path(bucket, warehouseKeyPrefix)), @@ -1489,21 +1436,6 @@ public void testSessionTagsWithEmptyContext() { String bucket = "bucket"; String warehouseKeyPrefix = "path/to/warehouse"; - RealmConfig sessionTagsEnabledConfig = - new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, - () -> "realm"); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); Mockito.when(stsClient.assumeRole(requestCaptor.capture())).thenReturn(ASSUME_ROLE_RESPONSE); @@ -1517,7 +1449,7 @@ public String getConfiguration(@Nonnull RealmContext ctx, String configName) { .build(), stsClient) .getSubscopedCreds( - sessionTagsEnabledConfig, + SESSION_TAGS_ENABLED_CONFIG, true, Set.of(s3Path(bucket, warehouseKeyPrefix)), Set.of(s3Path(bucket, warehouseKeyPrefix)), @@ -1563,17 +1495,10 @@ public void testSessionTagsAccessDeniedGracefulHandling() { RealmConfig sessionTagsEnabledConfig = new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key())) { - return "true"; - } - return null; - } - }, + RealmConfigurationSource.global( + Map.of( + FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), + "true")), () -> "realm"); // Simulate STS throwing AccessDeniedException when sts:TagSession is not allowed diff --git a/runtime/admin/src/main/java/org/apache/polaris/admintool/config/AdminToolProducers.java b/runtime/admin/src/main/java/org/apache/polaris/admintool/config/AdminToolProducers.java index 236325a588..a65324cbe5 100644 --- a/runtime/admin/src/main/java/org/apache/polaris/admintool/config/AdminToolProducers.java +++ b/runtime/admin/src/main/java/org/apache/polaris/admintool/config/AdminToolProducers.java @@ -28,9 +28,9 @@ import java.util.UUID; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.PolarisStorageIntegration; @@ -74,15 +74,15 @@ PolarisStorageIntegration getStorageIntegrationForConfig( } @Produces - public PolarisConfigurationStore configurationStore() { - // A configuration store is not required when running the admin tool. - return new PolarisConfigurationStore() {}; + public RealmConfigurationSource configurationStore() { + // A configuration source is not required when running the admin tool. + return RealmConfigurationSource.EMPTY_CONFIG; } @Produces - public RealmConfig dummyRealmConfig(PolarisConfigurationStore configurationStore) { + public RealmConfig dummyRealmConfig(RealmConfigurationSource configurationSource) { // Use a random realm ID for RealmConfig since the PolarisConfigurationStore is empty anyway String absentId = UUID.randomUUID().toString(); - return new RealmConfigImpl(configurationStore, () -> absentId); + return new RealmConfigImpl(configurationSource, () -> absentId); } } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java index 02d7a2f223..c59da2d8af 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java @@ -26,12 +26,14 @@ import java.util.Map; import java.util.Optional; import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ApplicationScoped -public class DefaultConfigurationStore implements PolarisConfigurationStore { +public class DefaultConfigurationStore + implements PolarisConfigurationStore, RealmConfigurationSource { Logger LOGGER = LoggerFactory.getLogger(DefaultConfigurationStore.class); private final Map defaults; @@ -47,15 +49,18 @@ public DefaultConfigurationStore( } @Override - public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { + public @Nullable Object getConfigValue(@Nonnull RealmContext realmContext, String configName) { String realm = realmContext.getRealmIdentifier(); LOGGER.debug("Get configuration value for {} with realm {}", configName, realm); + return Optional.ofNullable(realmOverrides.getOrDefault(realm, Map.of()).get(configName)) + .orElseGet(() -> getDefaultConfiguration(configName)); + } + + @Override + public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { @SuppressWarnings("unchecked") - T confgValue = - (T) - Optional.ofNullable(realmOverrides.getOrDefault(realm, Map.of()).get(configName)) - .orElseGet(() -> getDefaultConfiguration(configName)); - return confgValue; + T value = (T) getConfigValue(realmContext, configName); + return value; } private @Nullable T getDefaultConfiguration(String configName) { diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java index 121eb382c1..1dc021243c 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java @@ -38,9 +38,9 @@ import org.apache.polaris.core.auth.DefaultPolarisAuthorizerFactory; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerFactory; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.credentials.PolarisCredentialManager; @@ -123,17 +123,17 @@ public PolarisDiagnostics polarisDiagnostics() { @RequestScoped public CallContext polarisCallContext( RealmContext realmContext, - PolarisConfigurationStore configurationStore, + RealmConfigurationSource configurationSource, MetaStoreManagerFactory metaStoreManagerFactory) { BasePersistence metaStoreSession = metaStoreManagerFactory.getOrCreateSession(realmContext); - return new PolarisCallContext(realmContext, metaStoreSession, configurationStore); + return new PolarisCallContext(realmContext, metaStoreSession, configurationSource); } @Produces @RequestScoped public RealmConfig realmConfig( - RealmContext realmContext, PolarisConfigurationStore configurationStore) { - return new RealmConfigImpl(configurationStore, realmContext); + RealmContext realmContext, RealmConfigurationSource configurationSource) { + return new RealmConfigImpl(configurationSource, realmContext); } @Produces diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java index 91af34e5bc..ed32c09b32 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java @@ -1139,14 +1139,16 @@ private IcebergCatalogHandler newWrapperWithFineLevelAuthDisabled( // Create a simple RealmConfig implementation that overrides just what we need RealmConfig customRealmConfig = new RealmConfig() { + @SuppressWarnings("removal") @Override public T getConfig(String configName) { - return realmConfig.getConfig(configName); + throw new UnsupportedOperationException(); } + @SuppressWarnings("removal") @Override public T getConfig(String configName, T defaultValue) { - return realmConfig.getConfig(configName, defaultValue); + throw new UnsupportedOperationException(); } @Override diff --git a/runtime/service/src/test/java/org/apache/polaris/service/entity/CatalogEntityTest.java b/runtime/service/src/test/java/org/apache/polaris/service/entity/CatalogEntityTest.java index fdc71d5ac5..ec90da786b 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/entity/CatalogEntityTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/entity/CatalogEntityTest.java @@ -18,6 +18,7 @@ */ package org.apache.polaris.service.entity; +import static org.apache.polaris.core.config.RealmConfigurationSource.EMPTY_CONFIG; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.core.JsonProcessingException; @@ -39,7 +40,6 @@ import org.apache.polaris.core.admin.model.ServiceIdentityInfo; import org.apache.polaris.core.admin.model.SigV4AuthenticationParameters; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.context.RealmContext; @@ -65,7 +65,7 @@ public class CatalogEntityTest { @BeforeEach public void setup() { RealmContext realmContext = () -> "realm"; - this.realmConfig = new RealmConfigImpl(new PolarisConfigurationStore() {}, realmContext); + this.realmConfig = new RealmConfigImpl(EMPTY_CONFIG, realmContext); this.serviceIdentityProvider = Mockito.mock(ServiceIdentityProvider.class); Mockito.when(serviceIdentityProvider.getServiceIdentityInfo(Mockito.any())) .thenReturn( diff --git a/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestFixture.java b/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestFixture.java index 19900e1200..e54f42e01b 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestFixture.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestFixture.java @@ -107,7 +107,7 @@ private PolarisPrincipalSecrets fetchAdminSecrets() { BasePersistence metaStoreSession = helper.metaStoreManagerFactory.getOrCreateSession(realmContext); PolarisCallContext polarisContext = - new PolarisCallContext(realmContext, metaStoreSession, helper.configurationStore); + new PolarisCallContext(realmContext, metaStoreSession, helper.configurationSource); PolarisMetaStoreManager metaStoreManager = helper.metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); PrincipalEntity principal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestHelper.java b/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestHelper.java index 281b15b5f1..4f97c191e8 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestHelper.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/test/PolarisIntegrationTestHelper.java @@ -21,7 +21,7 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.config.PolarisConfigurationStore; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.service.context.RealmContextResolver; import org.junit.jupiter.api.TestInfo; @@ -32,7 +32,7 @@ public class PolarisIntegrationTestHelper { @Inject MetaStoreManagerFactory metaStoreManagerFactory; @Inject RealmContextResolver realmContextResolver; @Inject PolarisDiagnostics diagServices; - @Inject PolarisConfigurationStore configurationStore; + @Inject RealmConfigurationSource configurationSource; public PolarisIntegrationTestFixture createFixture(TestEnvironment testEnv, TestInfo testInfo) { return new PolarisIntegrationTestFixture(this, testEnv, testInfo); diff --git a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java index b6572b2d66..bdace7c672 100644 --- a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java +++ b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java @@ -23,8 +23,6 @@ import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import jakarta.enterprise.inject.Instance; import jakarta.ws.rs.core.SecurityContext; import java.security.Principal; @@ -41,8 +39,8 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.ExternalCatalogFactory; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; +import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.credentials.PolarisCredentialManager; @@ -109,7 +107,7 @@ public record TestServices( IcebergRestCatalogApi restApi, IcebergRestConfigurationApi restConfigurationApi, IcebergCatalogAdapter catalogAdapter, - PolarisConfigurationStore configurationStore, + RealmConfigurationSource configurationSource, PolarisDiagnostics polarisDiagnostics, StorageCredentialCache storageCredentialCache, ResolverFactory resolverFactory, @@ -129,21 +127,6 @@ public record TestServices( private static final RealmContext TEST_REALM = () -> "test-realm"; private static final String GCP_ACCESS_TOKEN = "abc"; - private static class MockedConfigurationStore implements PolarisConfigurationStore { - private final Map defaults; - - public MockedConfigurationStore(Map defaults) { - this.defaults = Map.copyOf(defaults); - } - - @Override - public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { - @SuppressWarnings("unchecked") - T confgValue = (T) defaults.get(configName); - return confgValue; - } - } - public static Builder builder() { return new Builder(); } @@ -211,7 +194,7 @@ public Builder withEventDelegator(boolean useEventDelegator) { } public TestServices build() { - PolarisConfigurationStore configurationStore = new MockedConfigurationStore(config); + RealmConfigurationSource configurationSource = RealmConfigurationSource.global(config); PolarisAuthorizer authorizer = Mockito.mock(PolarisAuthorizer.class); // Application level @@ -233,7 +216,7 @@ public TestServices build() { BasePersistence metaStoreSession = metaStoreManagerFactory.getOrCreateSession(realmContext); CallContext callContext = - new PolarisCallContext(realmContext, metaStoreSession, configurationStore); + new PolarisCallContext(realmContext, metaStoreSession, configurationSource); RealmConfig realmConfig = callContext.getRealmConfig(); PolarisMetaStoreManager metaStoreManager = @@ -416,7 +399,7 @@ public IcebergCatalogHandler createHandler( restApi, restConfigurationApi, catalogService, - configurationStore, + configurationSource, diagnostics, storageCredentialCache, resolverFactory, @@ -437,6 +420,6 @@ public IcebergCatalogHandler createHandler( public PolarisCallContext newCallContext() { BasePersistence metaStore = metaStoreManagerFactory.getOrCreateSession(realmContext); - return new PolarisCallContext(realmContext, metaStore, configurationStore); + return new PolarisCallContext(realmContext, metaStore, configurationSource); } } From 5c4a917aeccc1eb611b1a80bb5272888a2f8594d Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Wed, 4 Feb 2026 12:59:43 -0500 Subject: [PATCH 2/5] review: inline RealmConfigurationSource.global() --- .../core/config/RealmConfigurationSource.java | 5 ---- .../cache/StorageCredentialCacheTest.java | 9 ++---- .../AwsCredentialsStorageIntegrationTest.java | 29 +++++++++---------- .../apache/polaris/service/TestServices.java | 2 +- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java index e49ce4af7b..e7f84cc016 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java @@ -21,7 +21,6 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.util.Map; import org.apache.polaris.core.context.RealmContext; /** @@ -33,10 +32,6 @@ public interface RealmConfigurationSource { RealmConfigurationSource EMPTY_CONFIG = (rc, name) -> null; - static RealmConfigurationSource global(Map config) { - return (rc, name) -> config.get(name); - } - /** * Retrieve the current value for a configuration key for a given realm. May be null if not set. * diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java index 7a13e3c582..c26ee47fe7 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java @@ -18,6 +18,7 @@ */ package org.apache.polaris.core.storage.cache; +import static org.apache.polaris.core.config.FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL; import static org.apache.polaris.core.config.RealmConfigurationSource.EMPTY_CONFIG; import jakarta.annotation.Nonnull; @@ -31,10 +32,8 @@ import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; -import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; -import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -226,10 +225,8 @@ public void testCacheMissForAnotherPrincipal() { Mockito.when(storageCredentialsVendor.getRealmConfig()) .thenReturn( new RealmConfigImpl( - RealmConfigurationSource.global( - Map.of( - FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), - "true")), + (rc, name) -> + Map.of(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), () -> "realm")); testCacheForAnotherPrincipal(false); diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java index e9124d505f..b303a8e523 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -18,6 +18,9 @@ */ package org.apache.polaris.service.storage.aws; +import static org.apache.polaris.core.config.FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL; +import static org.apache.polaris.core.config.FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL; +import static org.apache.polaris.core.config.FeatureConfiguration.INCLUDE_TRACE_ID_IN_SESSION_TAGS; import static org.assertj.core.api.Assertions.assertThat; import jakarta.annotation.Nonnull; @@ -27,10 +30,8 @@ import java.util.Optional; import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; -import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; -import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.storage.BaseStorageIntegrationTest; import org.apache.polaris.core.storage.CredentialVendingContext; import org.apache.polaris.core.storage.StorageAccessConfig; @@ -64,17 +65,14 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { public static final RealmConfig PRINCIPAL_INCLUDER_REALM_CONFIG = new RealmConfigImpl( - RealmConfigurationSource.global( - Map.of( - FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), - "true")), + (rc, name) -> + Map.of(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), () -> "realm"); private static final RealmConfigImpl SESSION_TAGS_ENABLED_CONFIG = new RealmConfigImpl( - RealmConfigurationSource.global( - Map.of( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true")), + (rc, name) -> + Map.of(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), () -> "realm"); public static final AssumeRoleResponse ASSUME_ROLE_RESPONSE = @@ -1218,10 +1216,11 @@ public void testSessionTagsWithTraceIdWhenBothFlagsEnabled() { // Create a realm config with both session tags AND trace_id enabled RealmConfig sessionTagsAndTraceIdEnabledConfig = new RealmConfigImpl( - RealmConfigurationSource.global( + (rc, name) -> Map.of( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true", - FeatureConfiguration.INCLUDE_TRACE_ID_IN_SESSION_TAGS.key(), "true")), + INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true", + INCLUDE_TRACE_ID_IN_SESSION_TAGS.key(), "true") + .get(name), () -> "realm"); ArgumentCaptor requestCaptor = @@ -1495,10 +1494,8 @@ public void testSessionTagsAccessDeniedGracefulHandling() { RealmConfig sessionTagsEnabledConfig = new RealmConfigImpl( - RealmConfigurationSource.global( - Map.of( - FeatureConfiguration.INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), - "true")), + (rc, name) -> + Map.of(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), () -> "realm"); // Simulate STS throwing AccessDeniedException when sts:TagSession is not allowed diff --git a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java index bdace7c672..e18e8207f9 100644 --- a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java +++ b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java @@ -194,7 +194,7 @@ public Builder withEventDelegator(boolean useEventDelegator) { } public TestServices build() { - RealmConfigurationSource configurationSource = RealmConfigurationSource.global(config); + RealmConfigurationSource configurationSource = (rc, name) -> config.get(name); PolarisAuthorizer authorizer = Mockito.mock(PolarisAuthorizer.class); // Application level From 6e97af49ce6727e227c58d5428a9576f62a82f4d Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Thu, 5 Feb 2026 14:12:16 -0500 Subject: [PATCH 3/5] review: deprecate PolarisConfigurationStore --- CHANGELOG.md | 1 + .../polaris/core/PolarisCallContext.java | 4 +- .../config/PolarisConfigurationStore.java | 8 ++-- .../PolarisConfigurationStoreTest.java | 1 + .../config/DefaultConfigurationStore.java | 11 +---- .../PolarisConfigurationStoreBridge.java | 46 +++++++++++++++++++ .../config/DefaultConfigurationStoreTest.java | 15 ++++-- 7 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5584ee7d..2bb892d0ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ request adding CHANGELOG notes for breaking (!) changes and possibly other secti ### Deprecations - The configuration option `polaris.rate-limiter.token-bucket.window` is no longer supported and should be removed. +- `PolarisConfigurationStore` has been deprecated for removal. ### Fixes diff --git a/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java b/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java index 590ed28abd..d260f6979d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/PolarisCallContext.java @@ -19,7 +19,6 @@ package org.apache.polaris.core; import jakarta.annotation.Nonnull; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.config.RealmConfigurationSource; @@ -43,11 +42,12 @@ public class PolarisCallContext implements CallContext { * @deprecated Use {@link PolarisCallContext#PolarisCallContext(RealmContext, BasePersistence, * RealmConfigurationSource)}. */ + @SuppressWarnings("removal") @Deprecated(forRemoval = true) public PolarisCallContext( @Nonnull RealmContext realmContext, @Nonnull BasePersistence metaStore, - @Nonnull PolarisConfigurationStore configurationStore) { + @Nonnull org.apache.polaris.core.config.PolarisConfigurationStore configurationStore) { this(realmContext, metaStore, configurationStore::getConfiguration); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java b/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java index 03bbe72c08..7da9b2eb14 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java @@ -29,7 +29,11 @@ /** * Dynamic configuration store used to retrieve runtime parameters, which may vary by realm or by * request. + * + * @deprecated Use {@link RealmConfig} instead. */ +@SuppressWarnings("DeprecatedIsStillUsed") +@Deprecated(forRemoval = true) public interface PolarisConfigurationStore { Logger LOGGER = LoggerFactory.getLogger(PolarisConfigurationStore.class); @@ -54,10 +58,8 @@ public interface PolarisConfigurationStore { * @param defaultValue the default value if the configuration key has no value * @return the current value or the supplied default value * @param the type of the configuration value - * @deprecated Use {@link RealmConfig}. */ - @SuppressWarnings({"DeprecatedIsStillUsed", "removal"}) - @Deprecated(forRemoval = true) + @SuppressWarnings("removal") default @Nonnull T getConfiguration( @Nonnull RealmContext realmContext, String configName, @Nonnull T defaultValue) { return asRealmConfig(realmContext).getConfig(configName, defaultValue); diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java index 612b8716bf..5bdc1dfc9d 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; /** Unit test for the default behaviors of the PolarisConfigurationStore interface. */ +@SuppressWarnings("removal") public class PolarisConfigurationStoreTest { private final RealmContext testRealmContext = () -> "testRealm"; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java index c59da2d8af..7a7dc12b9a 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java @@ -25,15 +25,13 @@ import jakarta.inject.Inject; import java.util.Map; import java.util.Optional; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfigurationSource; import org.apache.polaris.core.context.RealmContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ApplicationScoped -public class DefaultConfigurationStore - implements PolarisConfigurationStore, RealmConfigurationSource { +public class DefaultConfigurationStore implements RealmConfigurationSource { Logger LOGGER = LoggerFactory.getLogger(DefaultConfigurationStore.class); private final Map defaults; @@ -56,13 +54,6 @@ public DefaultConfigurationStore( .orElseGet(() -> getDefaultConfiguration(configName)); } - @Override - public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { - @SuppressWarnings("unchecked") - T value = (T) getConfigValue(realmContext, configName); - return value; - } - private @Nullable T getDefaultConfiguration(String configName) { @SuppressWarnings("unchecked") T confgValue = (T) defaults.get(configName); diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java b/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java new file mode 100644 index 0000000000..246929764d --- /dev/null +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.polaris.service.config; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.polaris.core.context.RealmContext; + +@SuppressWarnings("removal") +@ApplicationScoped +public class PolarisConfigurationStoreBridge + implements org.apache.polaris.core.config.PolarisConfigurationStore { + + private final DefaultConfigurationStore store; + + @Inject + public PolarisConfigurationStoreBridge(DefaultConfigurationStore store) { + this.store = store; + } + + @Override + public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { + @SuppressWarnings("unchecked") + T value = (T) store.getConfigValue(realmContext, configName); + return value; + } +} diff --git a/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java b/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java index e1f76417aa..a39d3219af 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java @@ -26,7 +26,6 @@ import jakarta.inject.Inject; import java.util.Map; import org.apache.polaris.core.config.FeatureConfiguration; -import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.assertj.core.api.Assertions; @@ -68,7 +67,10 @@ public Map getConfigOverrides() { private RealmContext realmContext; - @Inject PolarisConfigurationStore configurationStore; + @SuppressWarnings("removal") + @Inject + org.apache.polaris.core.config.PolarisConfigurationStore configurationStore; + @Inject FeaturesConfiguration featuresConfiguration; @BeforeEach @@ -81,6 +83,7 @@ public void before(TestInfo testInfo) { realmContext = () -> realmName; } + @SuppressWarnings("removal") @Test public void testGetConfiguration() { Object value = configurationStore.getConfiguration(realmContext, "missingKeyWithoutDefault"); @@ -97,6 +100,7 @@ public void testGetConfiguration() { .isTrue(); } + @SuppressWarnings("removal") @Test public void testGetRealmConfiguration() { // check the realmOne configuration @@ -118,6 +122,7 @@ public void testGetRealmConfiguration() { .isFalse(); } + @SuppressWarnings("removal") @Test void testGetConfigurationWithRealm() { // the falseByDefaultKey is set to `false` for all realms, but overwrite with value `true` for @@ -137,6 +142,7 @@ void testGetConfigurationWithRealm() { .isFalse(); } + @SuppressWarnings("removal") @Test public void testInjectedConfigurationStore() { // the default value for trueByDefaultKey is `true` @@ -152,7 +158,7 @@ public void testInjectedConfigurationStore() { Boolean realmOneValue = configurationStore.getConfiguration(realmOneContext, falseByDefaultKey); assertThat(realmOneValue).isTrue(); - assertThat(configurationStore).isInstanceOf(DefaultConfigurationStore.class); + assertThat(configurationStore).isInstanceOf(PolarisConfigurationStoreBridge.class); } @Test @@ -175,6 +181,7 @@ public void testInjectedFeaturesConfiguration() { .containsKey(trueByDefaultKey); } + @SuppressWarnings("removal") @Test public void testRegisterAndUseFeatureConfigurations() { String prefix = "testRegisterAndUseFeatureConfigurations"; @@ -187,6 +194,7 @@ public void testRegisterAndUseFeatureConfigurations() { .description(prefix) .buildFeatureConfiguration(); + @SuppressWarnings("deprecation") FeatureConfiguration unsafeConfig = FeatureConfiguration.builder() .key(String.format("%s_unsafe", prefix)) @@ -195,6 +203,7 @@ public void testRegisterAndUseFeatureConfigurations() { .description(prefix) .buildFeatureConfiguration(); + @SuppressWarnings("deprecation") FeatureConfiguration bothConfig = FeatureConfiguration.builder() .key(String.format("%s_both", prefix)) From eba3a9cb2469e84193fbfdc3342e0325420a71d1 Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Fri, 6 Feb 2026 11:21:57 -0500 Subject: [PATCH 4/5] review: remove PolarisConfigurationStoreBridge --- .../config/DefaultConfigurationStore.java | 11 ++++- .../PolarisConfigurationStoreBridge.java | 46 ------------------- .../config/DefaultConfigurationStoreTest.java | 2 +- 3 files changed, 11 insertions(+), 48 deletions(-) delete mode 100644 runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java index 7a7dc12b9a..cd86e4697d 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java @@ -30,8 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@SuppressWarnings("removal") @ApplicationScoped -public class DefaultConfigurationStore implements RealmConfigurationSource { +public class DefaultConfigurationStore + implements org.apache.polaris.core.config.PolarisConfigurationStore, RealmConfigurationSource { Logger LOGGER = LoggerFactory.getLogger(DefaultConfigurationStore.class); private final Map defaults; @@ -54,6 +56,13 @@ public DefaultConfigurationStore( .orElseGet(() -> getDefaultConfiguration(configName)); } + @Override + public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { + @SuppressWarnings("unchecked") + T value = (T) getConfigValue(realmContext, configName); + return value; + } + private @Nullable T getDefaultConfiguration(String configName) { @SuppressWarnings("unchecked") T confgValue = (T) defaults.get(configName); diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java b/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java deleted file mode 100644 index 246929764d..0000000000 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/PolarisConfigurationStoreBridge.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.polaris.service.config; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import org.apache.polaris.core.context.RealmContext; - -@SuppressWarnings("removal") -@ApplicationScoped -public class PolarisConfigurationStoreBridge - implements org.apache.polaris.core.config.PolarisConfigurationStore { - - private final DefaultConfigurationStore store; - - @Inject - public PolarisConfigurationStoreBridge(DefaultConfigurationStore store) { - this.store = store; - } - - @Override - public @Nullable T getConfiguration(@Nonnull RealmContext realmContext, String configName) { - @SuppressWarnings("unchecked") - T value = (T) store.getConfigValue(realmContext, configName); - return value; - } -} diff --git a/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java b/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java index a39d3219af..7a3b51644b 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java @@ -158,7 +158,7 @@ public void testInjectedConfigurationStore() { Boolean realmOneValue = configurationStore.getConfiguration(realmOneContext, falseByDefaultKey); assertThat(realmOneValue).isTrue(); - assertThat(configurationStore).isInstanceOf(PolarisConfigurationStoreBridge.class); + assertThat(configurationStore).isInstanceOf(DefaultConfigurationStore.class); } @Test From 3a9c9a993ac06b0b7bcd8518d4f6b46c52fc00ab Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Fri, 6 Feb 2026 11:41:52 -0500 Subject: [PATCH 5/5] review: avoid Map.of() when creating RealmConfig in tests --- .../cache/StorageCredentialCacheTest.java | 4 ++- .../AwsCredentialsStorageIntegrationTest.java | 36 +++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java index c26ee47fe7..15075db66b 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java @@ -226,7 +226,9 @@ public void testCacheMissForAnotherPrincipal() { .thenReturn( new RealmConfigImpl( (rc, name) -> - Map.of(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), + INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key().equals(name) + ? "true" + : null, () -> "realm")); testCacheForAnotherPrincipal(false); diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java index b303a8e523..81513896d4 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -25,11 +25,13 @@ import jakarta.annotation.Nonnull; import java.time.Instant; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.storage.BaseStorageIntegrationTest; @@ -64,16 +66,10 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { public static final Instant EXPIRE_TIME = Instant.now().plusMillis(3600_000); public static final RealmConfig PRINCIPAL_INCLUDER_REALM_CONFIG = - new RealmConfigImpl( - (rc, name) -> - Map.of(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), - () -> "realm"); + enabledFeatures(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL); - private static final RealmConfigImpl SESSION_TAGS_ENABLED_CONFIG = - new RealmConfigImpl( - (rc, name) -> - Map.of(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), - () -> "realm"); + private static final RealmConfig SESSION_TAGS_ENABLED_CONFIG = + enabledFeatures(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL); public static final AssumeRoleResponse ASSUME_ROLE_RESPONSE = AssumeRoleResponse.builder() @@ -89,6 +85,14 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { public static final PolarisPrincipal POLARIS_PRINCIPAL = PolarisPrincipal.of("test-principal", Map.of(), Set.of()); + @SafeVarargs + private static RealmConfig enabledFeatures(FeatureConfiguration... enabledOptions) { + return new RealmConfigImpl( + (rc, name) -> + Arrays.stream(enabledOptions).anyMatch(o -> o.key().equals(name)) ? "true" : null, + () -> "realm"); + } + @ParameterizedTest @ValueSource(strings = {"s3a", "s3"}) public void testGetSubscopedCreds(String scheme) { @@ -1215,13 +1219,8 @@ public void testSessionTagsWithTraceIdWhenBothFlagsEnabled() { // Create a realm config with both session tags AND trace_id enabled RealmConfig sessionTagsAndTraceIdEnabledConfig = - new RealmConfigImpl( - (rc, name) -> - Map.of( - INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true", - INCLUDE_TRACE_ID_IN_SESSION_TAGS.key(), "true") - .get(name), - () -> "realm"); + enabledFeatures( + INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL, INCLUDE_TRACE_ID_IN_SESSION_TAGS); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); @@ -1493,10 +1492,7 @@ public void testSessionTagsAccessDeniedGracefulHandling() { String warehouseKeyPrefix = "path/to/warehouse"; RealmConfig sessionTagsEnabledConfig = - new RealmConfigImpl( - (rc, name) -> - Map.of(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL.key(), "true").get(name), - () -> "realm"); + enabledFeatures(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL); // Simulate STS throwing AccessDeniedException when sts:TagSession is not allowed // In AWS SDK v2, this is represented as StsException with error code "AccessDenied"