Skip to content

Commit 6aaa546

Browse files
authored
Migrate catalog configs to the new reserved prefix (#1557)
* rewrite * rewrite * stable * changes per comments
1 parent 6455538 commit 6aaa546

File tree

7 files changed

+195
-34
lines changed

7 files changed

+195
-34
lines changed

polaris-core/src/main/java/org/apache/polaris/core/config/BehaviorChangeConfiguration.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@
3333
public class BehaviorChangeConfiguration<T> extends PolarisConfiguration<T> {
3434

3535
protected BehaviorChangeConfiguration(
36-
String key, String description, T defaultValue, Optional<String> catalogConfig) {
37-
super(key, description, defaultValue, catalogConfig);
36+
String key,
37+
String description,
38+
T defaultValue,
39+
Optional<String> catalogConfig,
40+
Optional<String> catalogConfigUnsafe) {
41+
super(key, description, defaultValue, catalogConfig, catalogConfigUnsafe);
3842
}
3943

4044
public static final BehaviorChangeConfiguration<Boolean> VALIDATE_VIEW_LOCATION_OVERLAP =

polaris-core/src/main/java/org/apache/polaris/core/config/FeatureConfiguration.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@
3434
*/
3535
public class FeatureConfiguration<T> extends PolarisConfiguration<T> {
3636
protected FeatureConfiguration(
37-
String key, String description, T defaultValue, Optional<String> catalogConfig) {
38-
super(key, description, defaultValue, catalogConfig);
37+
String key,
38+
String description,
39+
T defaultValue,
40+
Optional<String> catalogConfig,
41+
Optional<String> catalogConfigUnsafe) {
42+
super(key, description, defaultValue, catalogConfig, catalogConfigUnsafe);
3943
}
4044

4145
/**
@@ -82,7 +86,8 @@ public static void enforceFeatureEnabledOrThrow(
8286
public static final FeatureConfiguration<Boolean> ALLOW_TABLE_LOCATION_OVERLAP =
8387
PolarisConfiguration.<Boolean>builder()
8488
.key("ALLOW_TABLE_LOCATION_OVERLAP")
85-
.catalogConfig("allow.overlapping.table.location")
89+
.catalogConfig("polaris.config.allow.overlapping.table.location")
90+
.catalogConfigUnsafe("allow.overlapping.table.location")
8691
.description(
8792
"If set to true, allow one table's location to reside within another table's location. "
8893
+ "This is only enforced within a given namespace.")
@@ -116,15 +121,17 @@ public static void enforceFeatureEnabledOrThrow(
116121
public static final FeatureConfiguration<Boolean> ALLOW_UNSTRUCTURED_TABLE_LOCATION =
117122
PolarisConfiguration.<Boolean>builder()
118123
.key("ALLOW_UNSTRUCTURED_TABLE_LOCATION")
119-
.catalogConfig("allow.unstructured.table.location")
124+
.catalogConfig("polaris.config.allow.unstructured.table.location")
125+
.catalogConfigUnsafe("allow.unstructured.table.location")
120126
.description("If set to true, allows unstructured table locations.")
121127
.defaultValue(false)
122128
.buildFeatureConfiguration();
123129

124130
public static final FeatureConfiguration<Boolean> ALLOW_EXTERNAL_TABLE_LOCATION =
125131
PolarisConfiguration.<Boolean>builder()
126132
.key("ALLOW_EXTERNAL_TABLE_LOCATION")
127-
.catalogConfig("allow.external.table.location")
133+
.catalogConfig("polaris.config.allow.external.table.location")
134+
.catalogConfigUnsafe("allow.external.table.location")
128135
.description(
129136
"If set to true, allows tables to have external locations outside the default structure.")
130137
.defaultValue(false)
@@ -133,15 +140,17 @@ public static void enforceFeatureEnabledOrThrow(
133140
public static final FeatureConfiguration<Boolean> ALLOW_EXTERNAL_CATALOG_CREDENTIAL_VENDING =
134141
PolarisConfiguration.<Boolean>builder()
135142
.key("ALLOW_EXTERNAL_CATALOG_CREDENTIAL_VENDING")
136-
.catalogConfig("enable.credential.vending")
143+
.catalogConfig("polaris.config.enable.credential.vending")
144+
.catalogConfigUnsafe("enable.credential.vending")
137145
.description("If set to true, allow credential vending for external catalogs.")
138146
.defaultValue(true)
139147
.buildFeatureConfiguration();
140148

141149
public static final FeatureConfiguration<List<String>> SUPPORTED_CATALOG_STORAGE_TYPES =
142150
PolarisConfiguration.<List<String>>builder()
143151
.key("SUPPORTED_CATALOG_STORAGE_TYPES")
144-
.catalogConfig("supported.storage.types")
152+
.catalogConfig("polaris.config.supported.storage.types")
153+
.catalogConfigUnsafe("supported.storage.types")
145154
.description("The list of supported storage types for a catalog")
146155
.defaultValue(
147156
List.of(
@@ -154,23 +163,26 @@ public static void enforceFeatureEnabledOrThrow(
154163
public static final FeatureConfiguration<Boolean> CLEANUP_ON_NAMESPACE_DROP =
155164
PolarisConfiguration.<Boolean>builder()
156165
.key("CLEANUP_ON_NAMESPACE_DROP")
157-
.catalogConfig("cleanup.on.namespace.drop")
166+
.catalogConfig("polaris.config.cleanup.on.namespace.drop")
167+
.catalogConfigUnsafe("cleanup.on.namespace.drop")
158168
.description("If set to true, clean up data when a namespace is dropped")
159169
.defaultValue(false)
160170
.buildFeatureConfiguration();
161171

162172
public static final FeatureConfiguration<Boolean> CLEANUP_ON_CATALOG_DROP =
163173
PolarisConfiguration.<Boolean>builder()
164174
.key("CLEANUP_ON_CATALOG_DROP")
165-
.catalogConfig("cleanup.on.catalog.drop")
175+
.catalogConfig("polaris.config.cleanup.on.catalog.drop")
176+
.catalogConfigUnsafe("cleanup.on.catalog.drop")
166177
.description("If set to true, clean up data when a catalog is dropped")
167178
.defaultValue(false)
168179
.buildFeatureConfiguration();
169180

170181
public static final FeatureConfiguration<Boolean> DROP_WITH_PURGE_ENABLED =
171182
PolarisConfiguration.<Boolean>builder()
172183
.key("DROP_WITH_PURGE_ENABLED")
173-
.catalogConfig("drop-with-purge.enabled")
184+
.catalogConfig("polaris.config.drop-with-purge.enabled")
185+
.catalogConfigUnsafe("drop-with-purge.enabled")
174186
.description(
175187
"If set to true, allows tables to be dropped with the purge parameter set to true.")
176188
.defaultValue(true)

polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfiguration.java

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,81 @@
2121
import java.util.ArrayList;
2222
import java.util.List;
2323
import java.util.Optional;
24+
import java.util.function.Function;
25+
import java.util.stream.Collectors;
26+
import java.util.stream.Stream;
2427
import org.apache.polaris.core.context.CallContext;
2528
import org.slf4j.Logger;
2629
import org.slf4j.LoggerFactory;
2730

2831
/**
29-
* An ABC for Polaris configurations that alter the service's behavior
32+
* An ABC for Polaris configurations that alter the service's behavior TODO: deprecate unsafe
33+
* catalog configs and remove related code
3034
*
3135
* @param <T> The type of the configuration
3236
*/
3337
public abstract class PolarisConfiguration<T> {
3438

3539
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfiguration.class);
3640

41+
private static final List<PolarisConfiguration<?>> allConfigurations = new ArrayList<>();
42+
3743
public final String key;
3844
public final String description;
3945
public final T defaultValue;
4046
private final Optional<String> catalogConfigImpl;
47+
private final Optional<String> catalogConfigUnsafeImpl;
4148
private final Class<T> typ;
4249

50+
/** catalog configs are expected to start with this prefix */
51+
private static final String SAFE_CATALOG_CONFIG_PREFIX = "polaris.config.";
52+
53+
/**
54+
* Helper method for building `allConfigurations` and checking for duplicate use of keys across
55+
* configs.
56+
*/
57+
private static void registerConfiguration(PolarisConfiguration<?> configuration) {
58+
for (PolarisConfiguration<?> existingConfiguration : allConfigurations) {
59+
if (existingConfiguration.key.equals(configuration.key)) {
60+
throw new IllegalArgumentException(
61+
String.format("Config '%s' is already in use", configuration.key));
62+
} else {
63+
var configs =
64+
Stream.of(
65+
configuration.catalogConfigImpl,
66+
configuration.catalogConfigUnsafeImpl,
67+
existingConfiguration.catalogConfigImpl,
68+
existingConfiguration.catalogConfigUnsafeImpl)
69+
.flatMap(Optional::stream)
70+
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
71+
for (var entry : configs.entrySet()) {
72+
if (entry.getValue() > 1) {
73+
throw new IllegalArgumentException(
74+
String.format("Catalog config %s is already in use", entry.getKey()));
75+
}
76+
}
77+
}
78+
}
79+
allConfigurations.add(configuration);
80+
}
81+
82+
/** Returns a list of all PolarisConfigurations that have been registered */
83+
public static List<PolarisConfiguration<?>> getAllConfigurations() {
84+
return List.copyOf(allConfigurations);
85+
}
86+
4387
@SuppressWarnings("unchecked")
4488
protected PolarisConfiguration(
45-
String key, String description, T defaultValue, Optional<String> catalogConfig) {
89+
String key,
90+
String description,
91+
T defaultValue,
92+
Optional<String> catalogConfig,
93+
Optional<String> catalogConfigUnsafe) {
4694
this.key = key;
4795
this.description = description;
4896
this.defaultValue = defaultValue;
4997
this.catalogConfigImpl = catalogConfig;
98+
this.catalogConfigUnsafeImpl = catalogConfigUnsafe;
5099
this.typ = (Class<T>) defaultValue.getClass();
51100
}
52101

@@ -61,6 +110,17 @@ public String catalogConfig() {
61110
"Attempted to read a catalog config key from a configuration that doesn't have one."));
62111
}
63112

113+
public boolean hasCatalogConfigUnsafe() {
114+
return catalogConfigUnsafeImpl.isPresent();
115+
}
116+
117+
public String catalogConfigUnsafe() {
118+
return catalogConfigUnsafeImpl.orElseThrow(
119+
() ->
120+
new IllegalStateException(
121+
"Attempted to read an unsafe catalog config key from a configuration that doesn't have one."));
122+
}
123+
64124
T cast(Object value) {
65125
return this.typ.cast(value);
66126
}
@@ -70,6 +130,7 @@ public static class Builder<T> {
70130
private String description;
71131
private T defaultValue;
72132
private Optional<String> catalogConfig = Optional.empty();
133+
private Optional<String> catalogConfigUnsafe = Optional.empty();
73134

74135
public Builder<T> key(String key) {
75136
this.key = key;
@@ -93,26 +154,53 @@ public Builder<T> defaultValue(T defaultValue) {
93154
}
94155

95156
public Builder<T> catalogConfig(String catalogConfig) {
157+
if (!catalogConfig.startsWith(SAFE_CATALOG_CONFIG_PREFIX)) {
158+
throw new IllegalArgumentException(
159+
"Catalog configs are expected to start with " + SAFE_CATALOG_CONFIG_PREFIX);
160+
}
96161
this.catalogConfig = Optional.of(catalogConfig);
97162
return this;
98163
}
99164

165+
/**
166+
* Used to support backwards compatability before there were reserved properties. Usage of this
167+
* method should be removed over time.
168+
*/
169+
@Deprecated
170+
public Builder<T> catalogConfigUnsafe(String catalogConfig) {
171+
LOGGER.info("catalogConfigUnsafe is deprecated! Use catalogConfig() instead.");
172+
if (catalogConfig.startsWith(SAFE_CATALOG_CONFIG_PREFIX)) {
173+
throw new IllegalArgumentException(
174+
"Unsafe catalog configs are not expected to start with " + SAFE_CATALOG_CONFIG_PREFIX);
175+
}
176+
this.catalogConfigUnsafe = Optional.of(catalogConfig);
177+
return this;
178+
}
179+
100180
public FeatureConfiguration<T> buildFeatureConfiguration() {
101181
if (key == null || description == null || defaultValue == null) {
102182
throw new IllegalArgumentException("key, description, and defaultValue are required");
103183
}
104-
return new FeatureConfiguration<>(key, description, defaultValue, catalogConfig);
184+
FeatureConfiguration<T> config =
185+
new FeatureConfiguration<>(
186+
key, description, defaultValue, catalogConfig, catalogConfigUnsafe);
187+
PolarisConfiguration.registerConfiguration(config);
188+
return config;
105189
}
106190

107191
public BehaviorChangeConfiguration<T> buildBehaviorChangeConfiguration() {
108192
if (key == null || description == null || defaultValue == null) {
109193
throw new IllegalArgumentException("key, description, and defaultValue are required");
110194
}
111-
if (catalogConfig.isPresent()) {
195+
if (catalogConfig.isPresent() || catalogConfigUnsafe.isPresent()) {
112196
throw new IllegalArgumentException(
113-
"catalogConfig is not valid for behavior change configs");
197+
"catalog configs are not valid for behavior change configs");
114198
}
115-
return new BehaviorChangeConfiguration<>(key, description, defaultValue, catalogConfig);
199+
BehaviorChangeConfiguration<T> config =
200+
new BehaviorChangeConfiguration<>(
201+
key, description, defaultValue, catalogConfig, catalogConfigUnsafe);
202+
PolarisConfiguration.registerConfiguration(config);
203+
return config;
116204
}
117205
}
118206

polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfigurationStore.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@
2323
import jakarta.annotation.Nullable;
2424
import java.util.ArrayList;
2525
import java.util.List;
26+
import java.util.Map;
2627
import org.apache.polaris.core.PolarisCallContext;
2728
import org.apache.polaris.core.entity.CatalogEntity;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
2831

2932
/**
3033
* Dynamic configuration store used to retrieve runtime parameters, which may vary by realm or by
3134
* request.
3235
*/
3336
public interface PolarisConfigurationStore {
37+
Logger LOGGER = LoggerFactory.getLogger(PolarisConfigurationStore.class);
38+
3439
/**
3540
* Retrieve the current value for a configuration key. May be null if not set.
3641
*
@@ -111,11 +116,22 @@ public interface PolarisConfigurationStore {
111116
PolarisCallContext ctx,
112117
@Nonnull CatalogEntity catalogEntity,
113118
PolarisConfiguration<T> config) {
114-
if (config.hasCatalogConfig()
115-
&& catalogEntity.getPropertiesAsMap().containsKey(config.catalogConfig())) {
116-
return tryCast(config, catalogEntity.getPropertiesAsMap().get(config.catalogConfig()));
117-
} else {
118-
return getConfiguration(ctx, config);
119+
if (config.hasCatalogConfig() || config.hasCatalogConfigUnsafe()) {
120+
Map<String, String> propertiesMap = catalogEntity.getPropertiesAsMap();
121+
String propertyValue = propertiesMap.get(config.catalogConfig());
122+
if (propertyValue == null) {
123+
propertyValue = propertiesMap.get(config.catalogConfigUnsafe());
124+
if (propertyValue != null) {
125+
LOGGER.warn(
126+
String.format(
127+
"Deprecated config %s is in use and will be removed in a future version",
128+
config.catalogConfigUnsafe()));
129+
}
130+
}
131+
if (propertyValue != null) {
132+
return tryCast(config, propertyValue);
133+
}
119134
}
135+
return getConfiguration(ctx, config);
120136
}
121137
}

polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void testConfigsCanBeCastedFromString() {
7575
public void testInvalidCastThrowsException() {
7676
// Bool not included because Boolean.valueOf turns non-boolean strings to false
7777
List<PolarisConfiguration<?>> configs =
78-
List.of(buildConfig("int", 12), buildConfig("long", 34L), buildConfig("double", 5.6D));
78+
List.of(buildConfig("int2", 12), buildConfig("long2", 34L), buildConfig("double2", 5.6D));
7979

8080
PolarisConfigurationStore store =
8181
new PolarisConfigurationStore() {
@@ -135,7 +135,7 @@ public void testBehaviorAndFeatureConfigs() {
135135

136136
BehaviorChangeConfiguration<Boolean> behaviorChangeConfig =
137137
PolarisConfiguration.<Boolean>builder()
138-
.key("example")
138+
.key("example2")
139139
.description("example")
140140
.defaultValue(true)
141141
.buildBehaviorChangeConfiguration();

quarkus/service/src/main/java/org/apache/polaris/service/quarkus/config/QuarkusReservedProperties.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
package org.apache.polaris.service.quarkus.config;
2020

2121
import io.smallrye.config.ConfigMapping;
22+
import java.util.HashSet;
2223
import java.util.List;
24+
import java.util.Set;
25+
import org.apache.polaris.core.config.PolarisConfiguration;
2326
import org.apache.polaris.service.config.ReservedProperties;
2427

2528
@ConfigMapping(prefix = "polaris.reserved-properties")
@@ -28,4 +31,28 @@ public interface QuarkusReservedProperties extends ReservedProperties {
2831
default List<String> prefixes() {
2932
return List.of("polaris.");
3033
}
34+
35+
@Override
36+
default Set<String> allowlist() {
37+
return AllowlistHolder.INSTANCE;
38+
}
39+
40+
class AllowlistHolder {
41+
static final Set<String> INSTANCE = computeAllowlist();
42+
43+
private static Set<String> computeAllowlist() {
44+
Set<String> allowlist = new HashSet<>();
45+
PolarisConfiguration.getAllConfigurations()
46+
.forEach(
47+
c -> {
48+
if (c.hasCatalogConfig()) {
49+
allowlist.add(c.catalogConfig());
50+
}
51+
if (c.hasCatalogConfigUnsafe()) {
52+
allowlist.add(c.catalogConfigUnsafe());
53+
}
54+
});
55+
return allowlist;
56+
}
57+
}
3158
}

0 commit comments

Comments
 (0)