Skip to content

Commit 0e1ec8a

Browse files
committed
Fix createViewWithCustomMetadataLocation tests for cloudTest task
This patch fixes the createViewWithCustomMetadataLocation tests for cloudTest tasks. The original test was generating temp directories internally, causing cloudTests to fail with BadRequestException instead of the expected ForbiddenException. Changes: - Switched to Hadoop's Path (Java Path removes slashes, e.g. s3://bucket/path -> s3:/bucket/path) - Made base classes abstract to avoid running them - Implemented createViewWithCustomMetadataLocation to allow passing a custom location Testing: - Verified locally
1 parent de79f18 commit 0e1ec8a

File tree

8 files changed

+113
-61
lines changed

8 files changed

+113
-61
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
import com.google.common.base.Strings;
2222
import java.util.List;
2323
import java.util.Optional;
24-
import java.util.stream.Stream;
24+
import org.apache.hadoop.fs.Path;
2525
import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
2626
import org.apache.polaris.core.admin.model.StorageConfigInfo;
2727

2828
/** Runs PolarisRestCatalogViewIntegrationTest on AWS. */
29-
public class PolarisRestCatalogViewAwsIntegrationTest
29+
public abstract class PolarisRestCatalogViewAwsIntegrationTestBase
3030
extends PolarisRestCatalogViewIntegrationBase {
3131
public static final String ROLE_ARN =
3232
Optional.ofNullable(System.getenv("INTEGRATION_TEST_ROLE_ARN")) // Backward compatibility
@@ -38,12 +38,12 @@ protected StorageConfigInfo getStorageConfigInfo() {
3838
return AwsStorageConfigInfo.builder()
3939
.setRoleArn(ROLE_ARN)
4040
.setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
41-
.setAllowedLocations(List.of(BASE_LOCATION))
41+
.setAllowedLocations(List.of(new Path(BASE_LOCATION, POLARIS_IT_SUBDIR).toString()))
4242
.build();
4343
}
4444

4545
@Override
46-
protected boolean shouldSkip() {
47-
return Stream.of(BASE_LOCATION, ROLE_ARN).anyMatch(Strings::isNullOrEmpty);
46+
protected String getCustomMetadataLocationDir() {
47+
return new Path(BASE_LOCATION, POLARIS_IT_CUSTOM_SUBDIR).toString();
4848
}
4949
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
import com.google.common.base.Strings;
2222
import java.util.List;
23-
import java.util.stream.Stream;
23+
import org.apache.hadoop.fs.Path;
2424
import org.apache.polaris.core.admin.model.AzureStorageConfigInfo;
2525
import org.apache.polaris.core.admin.model.StorageConfigInfo;
2626

2727
/** Runs PolarisRestCatalogViewIntegrationTest on Azure. */
28-
public class PolarisRestCatalogViewAzureIntegrationTest
28+
public abstract class PolarisRestCatalogViewAzureIntegrationTestBase
2929
extends PolarisRestCatalogViewIntegrationBase {
3030
public static final String TENANT_ID = System.getenv("INTEGRATION_TEST_AZURE_TENANT_ID");
3131
public static final String BASE_LOCATION = System.getenv("INTEGRATION_TEST_AZURE_PATH");
@@ -35,12 +35,12 @@ protected StorageConfigInfo getStorageConfigInfo() {
3535
return AzureStorageConfigInfo.builder()
3636
.setTenantId(TENANT_ID)
3737
.setStorageType(StorageConfigInfo.StorageTypeEnum.AZURE)
38-
.setAllowedLocations(List.of(BASE_LOCATION))
38+
.setAllowedLocations(List.of(new Path(BASE_LOCATION, POLARIS_IT_SUBDIR).toString()))
3939
.build();
4040
}
4141

4242
@Override
43-
protected boolean shouldSkip() {
44-
return Stream.of(BASE_LOCATION, TENANT_ID).anyMatch(Strings::isNullOrEmpty);
43+
protected String getCustomMetadataLocationDir() {
44+
return new Path(BASE_LOCATION, POLARIS_IT_CUSTOM_SUBDIR).toString();
4545
}
4646
}

integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewFileIntegrationTestBase.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import org.apache.polaris.core.admin.model.FileStorageConfigInfo;
2424
import org.apache.polaris.core.admin.model.StorageConfigInfo;
2525
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.BeforeEach;
2627
import org.junit.jupiter.api.io.TempDir;
2728

2829
/** Runs PolarisRestCatalogViewIntegrationTest on the local filesystem. */
2930
public abstract class PolarisRestCatalogViewFileIntegrationTestBase
3031
extends PolarisRestCatalogViewIntegrationBase {
3132
static String baseLocation;
33+
private Path tempDirForCustomMetadataLocation;
3234

3335
@BeforeAll
3436
public static void setUp(@TempDir Path tempDir) {
@@ -39,6 +41,11 @@ public static void setUp(@TempDir Path tempDir) {
3941
baseLocation = baseUri;
4042
}
4143

44+
@BeforeEach
45+
public void setUpTempDirForCustomMetadata(@TempDir Path tempDir) {
46+
this.tempDirForCustomMetadataLocation = tempDir;
47+
}
48+
4249
@Override
4350
protected StorageConfigInfo getStorageConfigInfo() {
4451
return FileStorageConfigInfo.builder()
@@ -48,7 +55,7 @@ protected StorageConfigInfo getStorageConfigInfo() {
4855
}
4956

5057
@Override
51-
protected boolean shouldSkip() {
52-
return false;
58+
protected String getCustomMetadataLocationDir() {
59+
return Path.of(tempDirForCustomMetadataLocation.toUri().toString()).toString();
5360
}
5461
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
import com.google.common.base.Strings;
2222
import java.util.List;
23-
import java.util.stream.Stream;
23+
import org.apache.hadoop.fs.Path;
2424
import org.apache.polaris.core.admin.model.GcpStorageConfigInfo;
2525
import org.apache.polaris.core.admin.model.StorageConfigInfo;
2626

2727
/** Runs PolarisRestCatalogViewIntegrationTest on GCP. */
28-
public class PolarisRestCatalogViewGcpIntegrationTest
28+
public abstract class PolarisRestCatalogViewGcpIntegrationTestBase
2929
extends PolarisRestCatalogViewIntegrationBase {
3030
public static final String SERVICE_ACCOUNT =
3131
System.getenv("INTEGRATION_TEST_GCS_SERVICE_ACCOUNT");
@@ -36,12 +36,12 @@ protected StorageConfigInfo getStorageConfigInfo() {
3636
return GcpStorageConfigInfo.builder()
3737
.setGcsServiceAccount(SERVICE_ACCOUNT)
3838
.setStorageType(StorageConfigInfo.StorageTypeEnum.GCS)
39-
.setAllowedLocations(List.of(BASE_LOCATION))
39+
.setAllowedLocations(List.of(new Path(BASE_LOCATION, POLARIS_IT_SUBDIR).toString()))
4040
.build();
4141
}
4242

4343
@Override
44-
protected boolean shouldSkip() {
45-
return Stream.of(BASE_LOCATION, SERVICE_ACCOUNT).anyMatch(Strings::isNullOrEmpty);
44+
protected String getCustomMetadataLocationDir() {
45+
return new Path(BASE_LOCATION, POLARIS_IT_CUSTOM_SUBDIR).toString();
4646
}
4747
}

integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222

2323
import com.google.common.collect.ImmutableMap;
2424
import java.lang.reflect.Method;
25-
import java.nio.file.Path;
26-
import java.nio.file.Paths;
2725
import java.util.Map;
26+
import org.apache.hadoop.fs.Path;
2827
import org.apache.iceberg.catalog.TableIdentifier;
2928
import org.apache.iceberg.exceptions.ForbiddenException;
3029
import org.apache.iceberg.rest.RESTCatalog;
3130
import org.apache.iceberg.view.BaseView;
3231
import org.apache.iceberg.view.View;
32+
import org.apache.iceberg.view.ViewBuilder;
3333
import org.apache.iceberg.view.ViewCatalogTests;
3434
import org.apache.polaris.core.admin.model.Catalog;
3535
import org.apache.polaris.core.admin.model.CatalogProperties;
@@ -45,6 +45,8 @@
4545
import org.apache.polaris.service.it.env.PolarisApiEndpoints;
4646
import org.apache.polaris.service.it.env.PolarisClient;
4747
import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
48+
import org.assertj.core.api.AbstractBooleanAssert;
49+
import org.assertj.core.api.AbstractStringAssert;
4850
import org.assertj.core.api.Assertions;
4951
import org.assertj.core.api.Assumptions;
5052
import org.assertj.core.configuration.PreferredAssumptionException;
@@ -55,7 +57,6 @@
5557
import org.junit.jupiter.api.Test;
5658
import org.junit.jupiter.api.TestInfo;
5759
import org.junit.jupiter.api.extension.ExtendWith;
58-
import org.junit.jupiter.api.io.TempDir;
5960

6061
/**
6162
* Import the full core Iceberg catalog tests by hitting the REST service via the RESTCatalog
@@ -86,6 +87,8 @@ public abstract class PolarisRestCatalogViewIntegrationBase extends ViewCatalogT
8687
private static PolarisApiEndpoints endpoints;
8788
private static PolarisClient client;
8889
private static ManagementApi managementApi;
90+
protected static final String POLARIS_IT_SUBDIR = "polaris_it";
91+
protected static final String POLARIS_IT_CUSTOM_SUBDIR = "polaris_it_custom";
8992

9093
private RESTCatalog restCatalog;
9194

@@ -104,8 +107,6 @@ static void close() throws Exception {
104107

105108
@BeforeEach
106109
public void before(TestInfo testInfo) {
107-
Assumptions.assumeThat(shouldSkip()).isFalse();
108-
109110
String principalName = client.newEntityName("snowman-rest");
110111
String principalRoleName = client.newEntityName("rest-admin");
111112
PrincipalWithCredentials principalCredentials =
@@ -157,12 +158,6 @@ public void cleanUp() {
157158
*/
158159
protected abstract StorageConfigInfo getStorageConfigInfo();
159160

160-
/**
161-
* @return Whether the tests should be skipped, for example due to environment variables not being
162-
* specified.
163-
*/
164-
protected abstract boolean shouldSkip();
165-
166161
@Override
167162
protected RESTCatalog catalog() {
168163
return restCatalog;
@@ -188,33 +183,84 @@ protected boolean overridesRequestedLocation() {
188183
return true;
189184
}
190185

191-
@Override
186+
protected String getCustomMetadataLocationDir() {
187+
return "";
188+
}
189+
192190
@Test
191+
@Override
193192
public void createViewWithCustomMetadataLocation() {
194-
Assertions.assertThatThrownBy(super::createViewWithCustomMetadataLocation)
193+
TableIdentifier identifier = TableIdentifier.of("ns", "view");
194+
String baseLocation = catalog().properties().get(CatalogEntity.DEFAULT_BASE_LOCATION_KEY);
195+
if (this.requiresNamespaceCreate()) {
196+
// Use the default baseLocation of the catalog. No "write.metadata.path" set.
197+
catalog().createNamespace(identifier.namespace());
198+
}
199+
200+
// Negative test, we cannot create views outside of base location
201+
((AbstractBooleanAssert)
202+
Assertions.assertThat(this.catalog().viewExists(identifier))
203+
.as("View should not exist", new Object[0]))
204+
.isFalse();
205+
ViewBuilder viewBuilder =
206+
catalog()
207+
.buildView(identifier)
208+
.withSchema(SCHEMA)
209+
.withDefaultNamespace(identifier.namespace())
210+
.withDefaultCatalog(catalog().name())
211+
.withQuery("spark", "select * from ns.tbl")
212+
.withProperty(
213+
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY,
214+
getCustomMetadataLocationDir())
215+
.withLocation(baseLocation);
216+
Assertions.assertThatThrownBy(viewBuilder::create)
195217
.isInstanceOf(ForbiddenException.class)
196218
.hasMessageContaining("Forbidden: Invalid locations");
219+
220+
// Positive, we can create views in the default base location's subdirectory
221+
String baseViewLocation = catalog().properties().get(CatalogEntity.DEFAULT_BASE_LOCATION_KEY);
222+
String baseCustomWriteMetadataLocation = baseViewLocation + "/custom_location";
223+
View view =
224+
this.catalog()
225+
.buildView(identifier)
226+
.withSchema(SCHEMA)
227+
.withDefaultNamespace(identifier.namespace())
228+
.withDefaultCatalog(this.catalog().name())
229+
.withQuery("spark", "select * from ns.tbl")
230+
.withProperty("write.metadata.path", baseCustomWriteMetadataLocation)
231+
.withLocation(baseViewLocation)
232+
.create();
233+
Assertions.assertThat(view).isNotNull();
234+
((AbstractBooleanAssert)
235+
Assertions.assertThat(this.catalog().viewExists(identifier))
236+
.as("View should exist", new Object[0]))
237+
.isTrue();
238+
Assertions.assertThat(view.properties())
239+
.containsEntry("write.metadata.path", baseCustomWriteMetadataLocation);
240+
((AbstractStringAssert)
241+
Assertions.assertThat(((BaseView) view).operations().current().metadataFileLocation())
242+
.isNotNull())
243+
.startsWith(new Path(baseCustomWriteMetadataLocation).toString());
197244
}
198245

199246
@Test
200-
public void createViewWithCustomMetadataLocationUsingPolaris(@TempDir Path tempDir) {
247+
public void createViewWithCustomMetadataLocationInheritedFromNamespace() {
201248
TableIdentifier identifier = TableIdentifier.of("ns", "view");
202-
203-
String location = Paths.get(tempDir.toUri().toString()).toString();
204-
String customLocation = Paths.get(tempDir.toUri().toString(), "custom-location").toString();
205-
String customLocation2 = Paths.get(tempDir.toUri().toString(), "custom-location2").toString();
206-
String customLocationChild =
207-
Paths.get(tempDir.toUri().toString(), "custom-location/child").toString();
208-
209-
catalog()
210-
.createNamespace(
211-
identifier.namespace(),
212-
ImmutableMap.of(
213-
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY, location));
214-
249+
String viewBaseLocation = getCustomMetadataLocationDir();
250+
String customWriteMetadataLocation = viewBaseLocation + "/custom-location";
251+
String customWriteMetadataLocation2 = viewBaseLocation + "/custom-location2";
252+
String customWriteMetadataLocationChild = viewBaseLocation + "/custom-location/child";
253+
if (this.requiresNamespaceCreate()) {
254+
// Views can inherit the namespace's "write.metadata.path" setting in Polaris.
255+
catalog()
256+
.createNamespace(
257+
identifier.namespace(),
258+
ImmutableMap.of(
259+
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY,
260+
viewBaseLocation));
261+
}
215262
Assertions.assertThat(catalog().viewExists(identifier)).as("View should not exist").isFalse();
216-
217-
// CAN create a view with a custom metadata location `baseLocation/customLocation`,
263+
// CAN create a view with a custom metadata location `viewLocation/customLocation`,
218264
// as long as the location is within the parent namespace's `write.metadata.path=baseLocation`
219265
View view =
220266
catalog()
@@ -224,17 +270,17 @@ public void createViewWithCustomMetadataLocationUsingPolaris(@TempDir Path tempD
224270
.withDefaultCatalog(catalog().name())
225271
.withQuery("spark", "select * from ns.tbl")
226272
.withProperty(
227-
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY, customLocation)
228-
.withLocation(location)
273+
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY,
274+
customWriteMetadataLocation)
275+
.withLocation(viewBaseLocation)
229276
.create();
230-
231277
Assertions.assertThat(view).isNotNull();
232278
Assertions.assertThat(catalog().viewExists(identifier)).as("View should exist").isTrue();
233-
Assertions.assertThat(view.properties()).containsEntry("write.metadata.path", customLocation);
279+
Assertions.assertThat(view.properties())
280+
.containsEntry("write.metadata.path", customWriteMetadataLocation);
234281
Assertions.assertThat(((BaseView) view).operations().current().metadataFileLocation())
235282
.isNotNull()
236-
.startsWith(customLocation);
237-
283+
.startsWith(customWriteMetadataLocation);
238284
// CANNOT update the view with a new metadata location `baseLocation/customLocation2`,
239285
// even though the new location is still under the parent namespace's
240286
// `write.metadata.path=baseLocation`.
@@ -245,11 +291,10 @@ public void createViewWithCustomMetadataLocationUsingPolaris(@TempDir Path tempD
245291
.updateProperties()
246292
.set(
247293
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY,
248-
customLocation2)
294+
customWriteMetadataLocation2)
249295
.commit())
250296
.isInstanceOf(ForbiddenException.class)
251297
.hasMessageContaining("Forbidden: Invalid locations");
252-
253298
// CANNOT update the view with a child metadata location `baseLocation/customLocation/child`,
254299
// even though it is a subpath of the original view's
255300
// `write.metadata.path=baseLocation/customLocation`.
@@ -260,7 +305,7 @@ public void createViewWithCustomMetadataLocationUsingPolaris(@TempDir Path tempD
260305
.updateProperties()
261306
.set(
262307
IcebergTableLikeEntity.USER_SPECIFIED_WRITE_METADATA_LOCATION_KEY,
263-
customLocationChild)
308+
customWriteMetadataLocationChild)
264309
.commit())
265310
.isInstanceOf(ForbiddenException.class)
266311
.hasMessageContaining("Forbidden: Invalid locations");

runtime/service/src/cloudTest/java/org/apache/polaris/service/it/RestCatalogViewAwsIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
import java.lang.reflect.Field;
2323
import java.nio.file.Path;
2424
import org.apache.iceberg.view.ViewCatalogTests;
25-
import org.apache.polaris.service.it.test.PolarisRestCatalogViewAwsIntegrationTest;
25+
import org.apache.polaris.service.it.test.PolarisRestCatalogViewAwsIntegrationTestBase;
2626
import org.junit.jupiter.api.BeforeEach;
2727
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
2828
import org.junit.jupiter.api.io.TempDir;
2929

3030
@QuarkusIntegrationTest
3131
@EnabledIfEnvironmentVariable(named = "INTEGRATION_TEST_S3_PATH", matches = ".+")
32-
public class RestCatalogViewAwsIT extends PolarisRestCatalogViewAwsIntegrationTest {
32+
public class RestCatalogViewAwsIT extends PolarisRestCatalogViewAwsIntegrationTestBase {
3333

3434
@BeforeEach
3535
public void setUpTempDir(@TempDir Path tempDir) throws Exception {

runtime/service/src/cloudTest/java/org/apache/polaris/service/it/RestCatalogViewAzureIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
import java.lang.reflect.Field;
2323
import java.nio.file.Path;
2424
import org.apache.iceberg.view.ViewCatalogTests;
25-
import org.apache.polaris.service.it.test.PolarisRestCatalogViewAzureIntegrationTest;
25+
import org.apache.polaris.service.it.test.PolarisRestCatalogViewAzureIntegrationTestBase;
2626
import org.junit.jupiter.api.BeforeEach;
2727
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
2828
import org.junit.jupiter.api.io.TempDir;
2929

3030
@QuarkusIntegrationTest
3131
@EnabledIfEnvironmentVariable(named = "INTEGRATION_TEST_AZURE_PATH", matches = ".+")
32-
public class RestCatalogViewAzureIT extends PolarisRestCatalogViewAzureIntegrationTest {
32+
public class RestCatalogViewAzureIT extends PolarisRestCatalogViewAzureIntegrationTestBase {
3333

3434
@BeforeEach
3535
public void setUpTempDir(@TempDir Path tempDir) throws Exception {

runtime/service/src/cloudTest/java/org/apache/polaris/service/it/RestCatalogViewGcpIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
import java.lang.reflect.Field;
2323
import java.nio.file.Path;
2424
import org.apache.iceberg.view.ViewCatalogTests;
25-
import org.apache.polaris.service.it.test.PolarisRestCatalogViewGcpIntegrationTest;
25+
import org.apache.polaris.service.it.test.PolarisRestCatalogViewGcpIntegrationTestBase;
2626
import org.junit.jupiter.api.BeforeEach;
2727
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
2828
import org.junit.jupiter.api.io.TempDir;
2929

3030
@QuarkusIntegrationTest
3131
@EnabledIfEnvironmentVariable(named = "INTEGRATION_TEST_GCS_PATH", matches = ".+")
32-
public class RestCatalogViewGcpIT extends PolarisRestCatalogViewGcpIntegrationTest {
32+
public class RestCatalogViewGcpIT extends PolarisRestCatalogViewGcpIntegrationTestBase {
3333

3434
@BeforeEach
3535
public void setUpTempDir(@TempDir Path tempDir) throws Exception {

0 commit comments

Comments
 (0)