Skip to content

Commit 4aea20e

Browse files
authored
Improve PolarisConfiguration (#97)
1 parent 919e9d8 commit 4aea20e

File tree

11 files changed

+242
-75
lines changed

11 files changed

+242
-75
lines changed

polaris-core/src/main/java/io/polaris/core/PolarisConfiguration.java

Lines changed: 126 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,136 @@
1515
*/
1616
package io.polaris.core;
1717

18-
public class PolarisConfiguration {
18+
import java.util.Optional;
1919

20-
public static final String ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING =
21-
"ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING";
22-
public static final String ALLOW_TABLE_LOCATION_OVERLAP = "ALLOW_TABLE_LOCATION_OVERLAP";
23-
public static final String ALLOW_NAMESPACE_LOCATION_OVERLAP = "ALLOW_NAMESPACE_LOCATION_OVERLAP";
24-
public static final String ALLOW_EXTERNAL_METADATA_FILE_LOCATION =
25-
"ALLOW_EXTERNAL_METADATA_FILE_LOCATION";
20+
public class PolarisConfiguration<T> {
2621

27-
public static final String ALLOW_OVERLAPPING_CATALOG_URLS = "ALLOW_OVERLAPPING_CATALOG_URLS";
22+
public final String key;
23+
public final String description;
24+
public final T defaultValue;
25+
private final Optional<String> catalogConfigImpl;
26+
private final Class<T> typ;
2827

29-
public static final String CATALOG_ALLOW_UNSTRUCTURED_TABLE_LOCATION =
30-
"allow.unstructured.table.location";
31-
public static final String CATALOG_ALLOW_EXTERNAL_TABLE_LOCATION =
32-
"allow.external.table.location";
28+
@SuppressWarnings("unchecked")
29+
public PolarisConfiguration(
30+
String key, String description, T defaultValue, Optional<String> catalogConfig) {
31+
this.key = key;
32+
this.description = description;
33+
this.defaultValue = defaultValue;
34+
this.catalogConfigImpl = catalogConfig;
35+
this.typ = (Class<T>) defaultValue.getClass();
36+
}
3337

34-
/*
35-
* Default values for the configuration properties
36-
*/
38+
public boolean hasCatalogConfig() {
39+
return catalogConfigImpl.isPresent();
40+
}
3741

38-
public static final boolean DEFAULT_ALLOW_OVERLAPPING_CATALOG_URLS = false;
39-
public static final boolean DEFAULT_ALLOW_TABLE_LOCATION_OVERLAP = false;
40-
public static final boolean DEFAULT_ALLOW_EXTERNAL_METADATA_FILE_LOCATION = false;
41-
public static final boolean DEFAULT_ALLOW_NAMESPACE_LOCATION_OVERLAP = false;
42+
public String catalogConfig() {
43+
return catalogConfigImpl.orElseThrow(
44+
() ->
45+
new IllegalStateException(
46+
"Attempted to read a catalog config key from a configuration that doesn't have one."));
47+
}
4248

43-
private PolarisConfiguration() {}
49+
T cast(Object value) {
50+
return this.typ.cast(value);
51+
}
52+
53+
public static class Builder<T> {
54+
private String key;
55+
private String description;
56+
private T defaultValue;
57+
private Optional<String> catalogConfig = Optional.empty();
58+
59+
public Builder<T> key(String key) {
60+
this.key = key;
61+
return this;
62+
}
63+
64+
public Builder<T> description(String description) {
65+
this.description = description;
66+
return this;
67+
}
68+
69+
public Builder<T> defaultValue(T defaultValue) {
70+
this.defaultValue = defaultValue;
71+
return this;
72+
}
73+
74+
public Builder<T> catalogConfig(String catalogConfig) {
75+
this.catalogConfig = Optional.of(catalogConfig);
76+
return this;
77+
}
78+
79+
public PolarisConfiguration<T> build() {
80+
if (key == null || description == null || defaultValue == null) {
81+
throw new IllegalArgumentException("key, description, and defaultValue are required");
82+
}
83+
return new PolarisConfiguration<>(key, description, defaultValue, catalogConfig);
84+
}
85+
}
86+
87+
public static <T> Builder<T> builder() {
88+
return new Builder<>();
89+
}
90+
91+
public static final PolarisConfiguration<Boolean>
92+
ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING =
93+
PolarisConfiguration.<Boolean>builder()
94+
.key("ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING")
95+
.description(
96+
"If set to true, require that principals must rotate their credentials before being used "
97+
+ "for anything else.")
98+
.defaultValue(false)
99+
.build();
100+
101+
public static final PolarisConfiguration<Boolean> ALLOW_TABLE_LOCATION_OVERLAP =
102+
PolarisConfiguration.<Boolean>builder()
103+
.key("ALLOW_TABLE_LOCATION_OVERLAP")
104+
.description(
105+
"If set to true, allow one table's location to reside within another table's location. "
106+
+ "This is only enforced within a given namespace.")
107+
.defaultValue(false)
108+
.build();
109+
110+
public static final PolarisConfiguration<Boolean> ALLOW_NAMESPACE_LOCATION_OVERLAP =
111+
PolarisConfiguration.<Boolean>builder()
112+
.key("ALLOW_NAMESPACE_LOCATION_OVERLAP")
113+
.description(
114+
"If set to true, allow one table's location to reside within another table's location. "
115+
+ "This is only enforced within a parent catalog or namespace.")
116+
.defaultValue(false)
117+
.build();
118+
119+
public static final PolarisConfiguration<Boolean> ALLOW_EXTERNAL_METADATA_FILE_LOCATION =
120+
PolarisConfiguration.<Boolean>builder()
121+
.key("ALLOW_EXTERNAL_METADATA_FILE_LOCATION")
122+
.description(
123+
"If set to true, allows metadata files to be located outside the default metadata directory.")
124+
.defaultValue(false)
125+
.build();
126+
127+
public static final PolarisConfiguration<Boolean> ALLOW_OVERLAPPING_CATALOG_URLS =
128+
PolarisConfiguration.<Boolean>builder()
129+
.key("ALLOW_OVERLAPPING_CATALOG_URLS")
130+
.description("If set to true, allows catalog URLs to overlap.")
131+
.defaultValue(false)
132+
.build();
133+
134+
public static final PolarisConfiguration<Boolean> ALLOW_UNSTRUCTURED_TABLE_LOCATION =
135+
PolarisConfiguration.<Boolean>builder()
136+
.key("ALLOW_UNSTRUCTURED_TABLE_LOCATION")
137+
.catalogConfig("allow.unstructured.table.location")
138+
.description("If set to true, allows unstructured table locations.")
139+
.defaultValue(false)
140+
.build();
141+
142+
public static final PolarisConfiguration<Boolean> ALLOW_EXTERNAL_TABLE_LOCATION =
143+
PolarisConfiguration.<Boolean>builder()
144+
.key("ALLOW_EXTERNAL_TABLE_LOCATION")
145+
.catalogConfig("allow.external.table.location")
146+
.description(
147+
"If set to true, allows tables to have external locations outside the default structure.")
148+
.defaultValue(false)
149+
.build();
44150
}

polaris-core/src/main/java/io/polaris/core/PolarisConfigurationStore.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616
package io.polaris.core;
1717

1818
import com.google.common.base.Preconditions;
19+
import io.polaris.core.entity.CatalogEntity;
1920
import org.jetbrains.annotations.NotNull;
2021
import org.jetbrains.annotations.Nullable;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
2124

2225
/**
2326
* Dynamic configuration store used to retrieve runtime parameters, which may vary by realm or by
2427
* request.
2528
*/
2629
public interface PolarisConfigurationStore {
30+
Logger LOGGER = LoggerFactory.getLogger(PolarisConfigurationStore.class);
2731

2832
/**
2933
* Retrieve the current value for a configuration key. May be null if not set.
@@ -53,4 +57,56 @@ public interface PolarisConfigurationStore {
5357
T configValue = getConfiguration(ctx, configName);
5458
return configValue != null ? configValue : defaultValue;
5559
}
60+
61+
/**
62+
* In some cases, we may extract a value that doesn't match the expected type for a config. This
63+
* method can be used to attempt to force-cast it using `String.valueOf`
64+
*/
65+
private <T> @NotNull T tryCast(PolarisConfiguration<T> config, Object value) {
66+
if (value == null) {
67+
return config.defaultValue;
68+
}
69+
70+
if (config.defaultValue instanceof Boolean) {
71+
return config.cast(Boolean.valueOf(String.valueOf(value)));
72+
} else {
73+
return config.cast(value);
74+
}
75+
}
76+
77+
/**
78+
* Retrieve the current value for a configuration.
79+
*
80+
* @param ctx the current call context
81+
* @param config the configuration to load
82+
* @return the current value set for the configuration key or null if not set
83+
* @param <T> the type of the configuration value
84+
*/
85+
default <T> @NotNull T getConfiguration(PolarisCallContext ctx, PolarisConfiguration<T> config) {
86+
T result = getConfiguration(ctx, config.key, config.defaultValue);
87+
return tryCast(config, result);
88+
}
89+
90+
/**
91+
* Retrieve the current value for a configuration, overriding with a catalog config if it is
92+
* present.
93+
*
94+
* @param ctx the current call context
95+
* @param catalogEntity the catalog to check for an override
96+
* @param config the configuration to load
97+
* @return the current value set for the configuration key or null if not set
98+
* @param <T> the type of the configuration value
99+
*/
100+
default <T> @NotNull T getConfiguration(
101+
PolarisCallContext ctx,
102+
@NotNull CatalogEntity catalogEntity,
103+
PolarisConfiguration<T> config) {
104+
if (config.hasCatalogConfig()
105+
&& catalogEntity.getPropertiesAsMap().containsKey(config.catalogConfig())) {
106+
LOGGER.debug("Loaded config from catalog: {}", config.catalogConfig());
107+
return tryCast(config, catalogEntity.getPropertiesAsMap().get(config.catalogConfig()));
108+
} else {
109+
return getConfiguration(ctx, config);
110+
}
111+
}
56112
}

polaris-core/src/main/java/io/polaris/core/auth/PolarisAuthorizer.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,7 @@ public void authorizeOrThrow(
500500
boolean enforceCredentialRotationRequiredState =
501501
featureConfig.getConfiguration(
502502
CallContext.getCurrentContext().getPolarisCallContext(),
503-
PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING,
504-
false);
503+
PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING);
505504
if (enforceCredentialRotationRequiredState
506505
&& authenticatedPrincipal
507506
.getPrincipalEntity()

polaris-core/src/main/java/io/polaris/core/storage/PolarisStorageConfigurationInfo.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.polaris.core.PolarisConfiguration;
2626
import io.polaris.core.PolarisDiagnostics;
2727
import io.polaris.core.admin.model.Catalog;
28+
import io.polaris.core.context.CallContext;
2829
import io.polaris.core.entity.CatalogEntity;
2930
import io.polaris.core.entity.PolarisEntity;
3031
import io.polaris.core.entity.PolarisEntityConstants;
@@ -147,19 +148,16 @@ public static Optional<PolarisStorageConfigurationInfo> forEntityPath(
147148
.orElse(null);
148149
CatalogEntity catalog = CatalogEntity.of(entityPath.get(0));
149150
boolean allowEscape =
150-
Optional.ofNullable(
151-
catalog
152-
.getPropertiesAsMap()
153-
.get(PolarisConfiguration.CATALOG_ALLOW_UNSTRUCTURED_TABLE_LOCATION))
154-
.map(
155-
val -> {
156-
LOGGER.debug(
157-
"Found catalog level property to allow unstructured table location: {}",
158-
val);
159-
return Boolean.parseBoolean(val);
160-
})
161-
.orElseGet(() -> Catalog.TypeEnum.EXTERNAL.equals(catalog.getCatalogType()));
162-
if (!allowEscape && baseLocation != null) {
151+
CallContext.getCurrentContext()
152+
.getPolarisCallContext()
153+
.getConfigurationStore()
154+
.getConfiguration(
155+
CallContext.getCurrentContext().getPolarisCallContext(),
156+
catalog,
157+
PolarisConfiguration.ALLOW_UNSTRUCTURED_TABLE_LOCATION);
158+
if (!allowEscape
159+
&& catalog.getCatalogType() != Catalog.TypeEnum.EXTERNAL
160+
&& baseLocation != null) {
163161
LOGGER.debug(
164162
"Not allowing unstructured table location for entity: {}",
165163
entityPath.getLast().getName());

polaris-service/src/main/java/io/polaris/service/admin/PolarisAdminService.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -496,14 +496,10 @@ private String terminateWithSlash(String path) {
496496
*/
497497
private boolean catalogOverlapsWithExistingCatalog(CatalogEntity catalogEntity) {
498498
boolean allowOverlappingCatalogUrls =
499-
Boolean.parseBoolean(
500-
String.valueOf(
501-
getCurrentPolarisContext()
502-
.getConfigurationStore()
503-
.getConfiguration(
504-
getCurrentPolarisContext(),
505-
PolarisConfiguration.ALLOW_OVERLAPPING_CATALOG_URLS,
506-
PolarisConfiguration.DEFAULT_ALLOW_OVERLAPPING_CATALOG_URLS)));
499+
getCurrentPolarisContext()
500+
.getConfigurationStore()
501+
.getConfiguration(
502+
getCurrentPolarisContext(), PolarisConfiguration.ALLOW_OVERLAPPING_CATALOG_URLS);
507503

508504
if (allowOverlappingCatalogUrls) {
509505
return false;

polaris-service/src/main/java/io/polaris/service/catalog/BasePolarisCatalog.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,7 @@ private void createNamespaceInternal(
431431
.getConfigurationStore()
432432
.getConfiguration(
433433
callContext.getPolarisCallContext(),
434-
PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP,
435-
PolarisConfiguration.DEFAULT_ALLOW_NAMESPACE_LOCATION_OVERLAP)) {
434+
PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) {
436435
LOG.debug("Validating no overlap for {} with sibling tables or namespaces", namespace);
437436
validateNoLocationOverlap(
438437
entity.getBaseLocation(), resolvedParent.getRawFullPath(), entity.getName());
@@ -577,8 +576,7 @@ public boolean setProperties(Namespace namespace, Map<String, String> properties
577576
.getConfigurationStore()
578577
.getConfiguration(
579578
callContext.getPolarisCallContext(),
580-
PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP,
581-
PolarisConfiguration.DEFAULT_ALLOW_NAMESPACE_LOCATION_OVERLAP)) {
579+
PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) {
582580
LOG.debug("Validating no overlap with sibling tables or namespaces");
583581
validateNoLocationOverlap(
584582
NamespaceEntity.of(updatedEntity).getBaseLocation(),
@@ -915,8 +913,7 @@ private void validateNoLocationOverlap(
915913
.getConfigurationStore()
916914
.getConfiguration(
917915
callContext.getPolarisCallContext(),
918-
PolarisConfiguration.ALLOW_TABLE_LOCATION_OVERLAP,
919-
PolarisConfiguration.DEFAULT_ALLOW_TABLE_LOCATION_OVERLAP)) {
916+
PolarisConfiguration.ALLOW_TABLE_LOCATION_OVERLAP)) {
920917
LOG.debug("Skipping location overlap validation for identifier '{}'", identifier);
921918
} else { // if (entity.getSubType().equals(PolarisEntitySubType.TABLE)) {
922919
// TODO - is this necessary for views? overlapping views do not expose subdirectories via the
@@ -1256,17 +1253,16 @@ protected String tableName() {
12561253
private void validateMetadataFileInTableDir(
12571254
TableIdentifier identifier, TableMetadata metadata, CatalogEntity catalog) {
12581255
PolarisCallContext polarisCallContext = callContext.getPolarisCallContext();
1259-
String allowEscape =
1260-
catalog
1261-
.getPropertiesAsMap()
1262-
.get(PolarisConfiguration.CATALOG_ALLOW_EXTERNAL_TABLE_LOCATION);
1263-
if (!Boolean.parseBoolean(allowEscape)
1256+
boolean allowEscape =
1257+
polarisCallContext
1258+
.getConfigurationStore()
1259+
.getConfiguration(
1260+
polarisCallContext, PolarisConfiguration.ALLOW_EXTERNAL_TABLE_LOCATION);
1261+
if (!allowEscape
12641262
&& !polarisCallContext
12651263
.getConfigurationStore()
12661264
.getConfiguration(
1267-
polarisCallContext,
1268-
PolarisConfiguration.ALLOW_EXTERNAL_METADATA_FILE_LOCATION,
1269-
PolarisConfiguration.DEFAULT_ALLOW_EXTERNAL_METADATA_FILE_LOCATION)) {
1265+
polarisCallContext, PolarisConfiguration.ALLOW_EXTERNAL_METADATA_FILE_LOCATION)) {
12701266
LOG.debug(
12711267
"Validating base location {} for table {} in metadata file {}",
12721268
metadata.location(),

polaris-service/src/test/java/io/polaris/service/admin/PolarisAuthzTestBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public abstract class PolarisAuthzTestBase {
127127
new PolarisAuthorizer(
128128
new DefaultConfigurationStore(
129129
Map.of(
130-
PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING,
130+
PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING.key,
131131
true)));
132132

133133
protected BasePolarisCatalog baseCatalog;

0 commit comments

Comments
 (0)