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/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..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,9 +19,9 @@ 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; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.BasePersistence; @@ -34,23 +34,36 @@ 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)}. + */ + @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); + } + + 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..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 @@ -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; @@ -32,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); @@ -58,35 +59,10 @@ public interface PolarisConfigurationStore { * @return the current value or the supplied default value * @param the type of the configuration value */ + @SuppressWarnings("removal") 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 +75,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 +113,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..e7f84cc016 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/config/RealmConfigurationSource.java @@ -0,0 +1,45 @@ +/* + * 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 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; + + /** + * 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..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 @@ -18,6 +18,9 @@ */ 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; import java.util.ArrayList; import java.util.Arrays; @@ -29,8 +32,6 @@ 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.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.context.RealmContext; @@ -54,8 +55,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 +225,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; - } - }, + (rc, 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/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/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..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 @@ -18,20 +18,22 @@ */ 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; 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.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.storage.BaseStorageIntegrationTest; import org.apache.polaris.core.storage.CredentialVendingContext; import org.apache.polaris.core.storage.StorageAccessConfig; @@ -64,19 +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( - 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; - } - }, - () -> "realm"); + enabledFeatures(INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL); + + private static final RealmConfig SESSION_TAGS_ENABLED_CONFIG = + enabledFeatures(INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL); public static final AssumeRoleResponse ASSUME_ROLE_RESPONSE = AssumeRoleResponse.builder() @@ -92,6 +85,14 @@ public String getConfiguration(@Nonnull RealmContext ctx, String configName) { 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) { @@ -1146,20 +1147,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); @@ -1231,23 +1219,8 @@ 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; - } - }, - () -> "realm"); + enabledFeatures( + INCLUDE_SESSION_TAGS_IN_SUBSCOPED_CREDENTIAL, INCLUDE_TRACE_ID_IN_SESSION_TAGS); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(AssumeRoleRequest.class); @@ -1361,20 +1334,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 +1388,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 +1408,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 +1434,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 +1447,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)), @@ -1562,19 +1492,7 @@ public void testSessionTagsAccessDeniedGracefulHandling() { 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"); + 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" 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..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 @@ -25,13 +25,15 @@ 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; +@SuppressWarnings("removal") @ApplicationScoped -public class DefaultConfigurationStore implements PolarisConfigurationStore { +public class DefaultConfigurationStore + implements org.apache.polaris.core.config.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/config/DefaultConfigurationStoreTest.java b/runtime/service/src/test/java/org/apache/polaris/service/config/DefaultConfigurationStoreTest.java index e1f76417aa..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 @@ -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` @@ -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)) 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..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 @@ -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 = (rc, name) -> config.get(name); 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); } }