diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/env/PolarisClient.java b/integration-tests/src/main/java/org/apache/polaris/service/it/env/PolarisClient.java index 1373c0d211..f3d60eeb16 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/env/PolarisClient.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/env/PolarisClient.java @@ -18,42 +18,47 @@ */ package org.apache.polaris.service.it.env; -import static java.util.concurrent.TimeUnit.MINUTES; +import static org.apache.polaris.service.it.ext.PolarisServerManagerLoader.polarisServerManager; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.ClientBuilder; +import java.util.Random; import org.apache.iceberg.rest.RESTSerializers; import org.apache.polaris.core.admin.model.PrincipalWithCredentials; public final class PolarisClient implements AutoCloseable { private final PolarisApiEndpoints endpoints; private final Client client; + // Use an alphanumeric ID for widest compatibility in HTTP and SQL. + // Use MAX_RADIX for shorter output. + private final String clientId = + Long.toString(Math.abs(new Random().nextLong()), Character.MAX_RADIX); private PolarisClient(PolarisApiEndpoints endpoints) { this.endpoints = endpoints; + this.client = polarisServerManager().createClient(); + } + + public static PolarisClient polarisClient(PolarisApiEndpoints endpoints) { + return new PolarisClient(endpoints); + } + + public static ObjectMapper buildObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.KebabCaseStrategy()); RESTSerializers.registerAll(mapper); - - this.client = - ClientBuilder.newBuilder() - .readTimeout(5, MINUTES) - .connectTimeout(1, MINUTES) - .register(new JacksonJsonProvider(mapper)) - .build(); + return mapper; } - public static PolarisClient polarisClient(PolarisApiEndpoints endpoints) { - return new PolarisClient(endpoints); + public String newEntityName(String hint) { + return polarisServerManager().transformEntityName(hint + "_" + clientId); } public ManagementApi managementApi(String authToken) { diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisIntegrationTestExtension.java b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisIntegrationTestExtension.java index 1432fd44f8..6429aaa4f6 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisIntegrationTestExtension.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisIntegrationTestExtension.java @@ -18,7 +18,8 @@ */ package org.apache.polaris.service.it.ext; -import java.util.ServiceLoader; +import static org.apache.polaris.service.it.ext.PolarisServerManagerLoader.polarisServerManager; + import org.apache.polaris.service.it.env.ClientCredentials; import org.apache.polaris.service.it.env.PolarisApiEndpoints; import org.apache.polaris.service.it.env.Server; @@ -34,11 +35,6 @@ public class PolarisIntegrationTestExtension implements ParameterResolver { private static final Namespace NAMESPACE = Namespace.create(PolarisIntegrationTestExtension.class); - private static final PolarisServerManager manager = - ServiceLoader.load(PolarisServerManager.class) - .findFirst() - .orElseThrow(() -> new IllegalStateException("PolarisServerManager not found")); - @Override public boolean supportsParameter( ParameterContext parameterContext, ExtensionContext extensionContext) @@ -66,7 +62,7 @@ private Env env(ExtensionContext context) { ExtensionContext classCtx = classContext(context); ExtensionContext.Store store = classCtx.getStore(NAMESPACE); return store.getOrComputeIfAbsent( - Env.class, (key) -> new Env(manager.serverForContext(classCtx)), Env.class); + Env.class, (key) -> new Env(polarisServerManager().serverForContext(classCtx)), Env.class); } private ExtensionContext classContext(ExtensionContext context) { diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManager.java b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManager.java index 3bdbb36883..79a3864595 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManager.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManager.java @@ -18,6 +18,12 @@ */ package org.apache.polaris.service.it.ext; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.apache.polaris.service.it.env.PolarisClient.buildObjectMapper; + +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; import org.apache.polaris.service.it.env.Server; import org.junit.jupiter.api.extension.ExtensionContext; @@ -31,4 +37,26 @@ public interface PolarisServerManager { * the argument to this call is closed. */ Server serverForContext(ExtensionContext context); + + /** Create a new HTTP client for accessing the server targeted by tests. */ + default Client createClient() { + return ClientBuilder.newBuilder() + .readTimeout(5, MINUTES) + .connectTimeout(1, MINUTES) + .register(new JacksonJsonProvider(buildObjectMapper())) + .build(); + } + + /** + * Transforms the name of an entity that tests need to create. Implementations may prepend of + * append text to the original name, but they should not alter the original name text or add any + * characters that have special meaning in Spark SQL, HTTP or Iceberg REST specifications. + * + *

This method will be called for all top-level entities (catalogs, principal, principal + * roles), but may not be called for secondary entities (such as catalog roles, namespaces, + * tables, etc.). + */ + default String transformEntityName(String name) { + return name; + } } diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManagerLoader.java b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManagerLoader.java new file mode 100644 index 0000000000..76879f880e --- /dev/null +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/ext/PolarisServerManagerLoader.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.it.ext; + +import java.util.ServiceLoader; + +public final class PolarisServerManagerLoader { + + private static final PolarisServerManager manager = + ServiceLoader.load(PolarisServerManager.class) + .findFirst() + .orElseThrow(() -> new IllegalStateException("PolarisServerManager not found")); + + private PolarisServerManagerLoader() {} + + public static PolarisServerManager polarisServerManager() { + return manager; + } +} diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisManagementServiceIntegrationTest.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisManagementServiceIntegrationTest.java index bdf5739845..0a8d0705fb 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisManagementServiceIntegrationTest.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisManagementServiceIntegrationTest.java @@ -190,7 +190,8 @@ public void testListCatalogs() { @Test public void testListCatalogsUnauthorized() { - PrincipalWithCredentials principal = managementApi.createPrincipal("a_new_user"); + PrincipalWithCredentials principal = + managementApi.createPrincipal(client.newEntityName("a_new_user")); try (Response response = client.managementApi(principal).request("v1/catalogs").get()) { assertThat(response).returns(Response.Status.FORBIDDEN.getStatusCode(), Response::getStatus); } @@ -480,7 +481,7 @@ public void testCreateExternalCatalog() { .setStorageType(StorageConfigInfo.StorageTypeEnum.S3) .setAllowedLocations(List.of("s3://my-old-bucket/path/to/data")) .build(); - String catalogName = "my-external-catalog"; + String catalogName = client.newEntityName("my-external-catalog"); String remoteUrl = "http://localhost:8080"; Catalog catalog = ExternalCatalog.builder() @@ -554,10 +555,11 @@ public void serialization() { public void testCreateAndUpdateAzureCatalog() { StorageConfigInfo storageConfig = new AzureStorageConfigInfo("azure:tenantid:12345", StorageConfigInfo.StorageTypeEnum.AZURE); + String catalogName = client.newEntityName("myazurecatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName("myazurecatalog") + .setName(catalogName) .setStorageConfigInfo(storageConfig) .setProperties(new CatalogProperties("abfss://container1@acct1.dfs.core.windows.net/")) .build(); @@ -567,11 +569,11 @@ public void testCreateAndUpdateAzureCatalog() { // 200 successful GET after creation Catalog fetchedCatalog; - try (Response response = managementApi.request("v1/catalogs/myazurecatalog").get()) { + try (Response response = managementApi.request("v1/catalogs/" + catalogName).get()) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = response.readEntity(Catalog.class); - assertThat(fetchedCatalog.getName()).isEqualTo("myazurecatalog"); + assertThat(fetchedCatalog.getName()).isEqualTo(catalogName); assertThat(fetchedCatalog.getProperties().toMap()) .isEqualTo( Map.of("default-base-location", "abfss://container1@acct1.dfs.core.windows.net/")); @@ -586,7 +588,7 @@ public void testCreateAndUpdateAzureCatalog() { Map.of("default-base-location", "abfss://newcontainer@acct1.dfs.core.windows.net/"), modifiedStorageConfig); try (Response response = - managementApi.request("v1/catalogs/myazurecatalog").put(Entity.json(badUpdateRequest))) { + managementApi.request("v1/catalogs/" + catalogName).put(Entity.json(badUpdateRequest))) { assertThat(response) .returns(Response.Status.BAD_REQUEST.getStatusCode(), Response::getStatus); ErrorResponse error = response.readEntity(ErrorResponse.class); @@ -605,7 +607,7 @@ public void testCreateAndUpdateAzureCatalog() { // 200 successful update try (Response response = - managementApi.request("v1/catalogs/myazurecatalog").put(Entity.json(updateRequest))) { + managementApi.request("v1/catalogs/" + catalogName).put(Entity.json(updateRequest))) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = response.readEntity(Catalog.class); @@ -615,7 +617,7 @@ public void testCreateAndUpdateAzureCatalog() { } // 204 Successful delete - try (Response response = managementApi.request("v1/catalogs/myazurecatalog").delete()) { + try (Response response = managementApi.request("v1/catalogs/" + catalogName).delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } } @@ -625,10 +627,11 @@ public void testCreateListUpdateAndDeleteCatalog() { StorageConfigInfo storageConfig = new AwsStorageConfigInfo( "arn:aws:iam::123456789011:role/role1", StorageConfigInfo.StorageTypeEnum.S3); + String catalogName = client.newEntityName("mycatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName("mycatalog") + .setName(catalogName) .setStorageConfigInfo(storageConfig) .setProperties(new CatalogProperties("s3://bucket1/")) .build(); @@ -643,11 +646,11 @@ public void testCreateListUpdateAndDeleteCatalog() { // 200 successful GET after creation Catalog fetchedCatalog; - try (Response response = managementApi.request("v1/catalogs/mycatalog").get()) { + try (Response response = managementApi.request("v1/catalogs/" + catalogName).get()) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = response.readEntity(Catalog.class); - assertThat(fetchedCatalog.getName()).isEqualTo("mycatalog"); + assertThat(fetchedCatalog.getName()).isEqualTo(catalogName); assertThat(fetchedCatalog.getProperties().toMap()) .isEqualTo(Map.of("default-base-location", "s3://bucket1/")); assertThat(fetchedCatalog.getEntityVersion()).isGreaterThan(0); @@ -661,7 +664,7 @@ public void testCreateListUpdateAndDeleteCatalog() { .extracting(Catalogs::getCatalogs) .asInstanceOf(InstanceOfAssertFactories.list(Catalog.class)) .filteredOn(cat -> !cat.getName().equalsIgnoreCase("ROOT")) - .satisfiesExactly(cat -> assertThat(cat).returns("mycatalog", Catalog::getName)); + .satisfiesExactly(cat -> assertThat(cat).returns(catalogName, Catalog::getName)); } // Reject update of fields that can't be currently updated @@ -674,7 +677,9 @@ public void testCreateListUpdateAndDeleteCatalog() { Map.of("default-base-location", "s3://newbucket/"), invalidModifiedStorageConfig); try (Response response = - managementApi.request("v1/catalogs/mycatalog").put(Entity.json(badUpdateRequest))) { + managementApi + .request("v1/catalogs/{cat}", Map.of("cat", catalogName)) + .put(Entity.json(badUpdateRequest))) { assertThat(response) .returns(Response.Status.BAD_REQUEST.getStatusCode(), Response::getStatus); ErrorResponse error = response.readEntity(ErrorResponse.class); @@ -699,7 +704,9 @@ public void testCreateListUpdateAndDeleteCatalog() { // 200 successful update try (Response response = - managementApi.request("v1/catalogs/mycatalog").put(Entity.json(updateRequest))) { + managementApi + .request("v1/catalogs/{cat}", Map.of("cat", catalogName)) + .put(Entity.json(updateRequest))) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = response.readEntity(Catalog.class); @@ -711,7 +718,8 @@ public void testCreateListUpdateAndDeleteCatalog() { } // 200 GET after update should show new properties - try (Response response = managementApi.request("v1/catalogs/mycatalog").get()) { + try (Response response = + managementApi.request("v1/catalogs/{cat}", Map.of("cat", catalogName)).get()) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = response.readEntity(Catalog.class); @@ -720,12 +728,14 @@ public void testCreateListUpdateAndDeleteCatalog() { } // 204 Successful delete - try (Response response = managementApi.request("v1/catalogs/mycatalog").delete()) { + try (Response response = + managementApi.request("v1/catalogs/{cat}", Map.of("cat", catalogName)).delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } // NOT_FOUND after deletion - try (Response response = managementApi.request("v1/catalogs/mycatalog").get()) { + try (Response response = + managementApi.request("v1/catalogs/{cat}", Map.of("cat", catalogName)).get()) { assertThat(response).returns(Response.Status.NOT_FOUND.getStatusCode(), Response::getStatus); } @@ -776,10 +786,11 @@ public void testGetCatalogInvalidName() { @Test public void testCatalogRoleInvalidName() { + String catalogName = client.newEntityName("mycatalog1"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName("mycatalog1") + .setName(catalogName) .setProperties(new CatalogProperties("s3://required/base/location")) .setStorageConfigInfo( new AwsStorageConfigInfo( @@ -813,7 +824,8 @@ public void testCatalogRoleInvalidName() { @Test public void testListPrincipalsUnauthorized() { - PrincipalWithCredentials principal = managementApi.createPrincipal("new_admin"); + PrincipalWithCredentials principal = + managementApi.createPrincipal(client.newEntityName("new_admin")); try (Response response = client.managementApi(principal).request("v1/principals").get()) { assertThat(response).returns(Response.Status.FORBIDDEN.getStatusCode(), Response::getStatus); } @@ -1180,10 +1192,11 @@ public void testGetPrincipalRoleInvalidName() { @Test public void testCreateListUpdateAndDeleteCatalogRole() { + String catalogName = client.newEntityName("mycatalog1"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName("mycatalog1") + .setName(catalogName) .setProperties(new CatalogProperties("s3://required/base/location")) .setStorageConfigInfo( new AwsStorageConfigInfo( @@ -1191,10 +1204,11 @@ public void testCreateListUpdateAndDeleteCatalogRole() { .build(); managementApi.createCatalog(catalog); + String catalogName2 = client.newEntityName("mycatalog2"); Catalog catalog2 = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName("mycatalog2") + .setName(catalogName2) .setStorageConfigInfo( new AwsStorageConfigInfo( "arn:aws:iam::012345678901:role/jdoe", StorageConfigInfo.StorageTypeEnum.S3)) @@ -1206,7 +1220,7 @@ public void testCreateListUpdateAndDeleteCatalogRole() { new CatalogRole("mycatalogrole", Map.of("custom-tag", "foo"), 0L, 0L, 1); try (Response response = managementApi - .request("v1/catalogs/mycatalog1/catalog-roles") + .request("v1/catalogs/{cat}/catalog-roles", Map.of("cat", catalogName)) .post(Entity.json(new CreateCatalogRoleRequest(catalogRole)))) { assertThat(response).returns(Response.Status.CREATED.getStatusCode(), Response::getStatus); @@ -1215,7 +1229,7 @@ public void testCreateListUpdateAndDeleteCatalogRole() { // Second attempt to create the same entity should fail with CONFLICT. try (Response response = managementApi - .request("v1/catalogs/mycatalog1/catalog-roles") + .request("v1/catalogs/{cat}/catalog-roles", Map.of("cat", catalogName)) .post(Entity.json(new CreateCatalogRoleRequest(catalogRole)))) { assertThat(response).returns(Response.Status.CONFLICT.getStatusCode(), Response::getStatus); @@ -1224,7 +1238,9 @@ public void testCreateListUpdateAndDeleteCatalogRole() { // 200 successful GET after creation CatalogRole fetchedCatalogRole; try (Response response = - managementApi.request("v1/catalogs/mycatalog1/catalog-roles/mycatalogrole").get()) { + managementApi + .request("v1/catalogs/{cat}/catalog-roles/mycatalogrole", Map.of("cat", catalogName)) + .get()) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalogRole = response.readEntity(CatalogRole.class); @@ -1235,7 +1251,10 @@ public void testCreateListUpdateAndDeleteCatalogRole() { } // Should list the catalogRole. - try (Response response = managementApi.request("v1/catalogs/mycatalog1/catalog-roles").get()) { + try (Response response = + managementApi + .request("v1/catalogs/{cat}/catalog-roles", Map.of("cat", catalogName)) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1246,7 +1265,10 @@ public void testCreateListUpdateAndDeleteCatalogRole() { } // Empty list if listing in catalog2 - try (Response response = managementApi.request("v1/catalogs/mycatalog2/catalog-roles").get()) { + try (Response response = + managementApi + .request("v1/catalogs/{cat}/catalog-roles", Map.of("cat", catalogName2)) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1268,7 +1290,7 @@ public void testCreateListUpdateAndDeleteCatalogRole() { // 200 successful update try (Response response = managementApi - .request("v1/catalogs/mycatalog1/catalog-roles/mycatalogrole") + .request("v1/catalogs/{cat}/catalog-roles/mycatalogrole", Map.of("cat", catalogName)) .put(Entity.json(updateRequest))) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalogRole = response.readEntity(CatalogRole.class); @@ -1278,7 +1300,9 @@ public void testCreateListUpdateAndDeleteCatalogRole() { // 200 GET after update should show new properties try (Response response = - managementApi.request("v1/catalogs/mycatalog1/catalog-roles/mycatalogrole").get()) { + managementApi + .request("v1/catalogs/{cat}/catalog-roles/mycatalogrole", Map.of("cat", catalogName)) + .get()) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalogRole = response.readEntity(CatalogRole.class); @@ -1287,20 +1311,27 @@ public void testCreateListUpdateAndDeleteCatalogRole() { // 204 Successful delete try (Response response = - managementApi.request("v1/catalogs/mycatalog1/catalog-roles/mycatalogrole").delete()) { + managementApi + .request("v1/catalogs/{cat}/catalog-roles/mycatalogrole", Map.of("cat", catalogName)) + .delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } // NOT_FOUND after deletion try (Response response = - managementApi.request("v1/catalogs/mycatalog1/catalog-roles/mycatalogrole").get()) { + managementApi + .request("v1/catalogs/{cat}/catalog-roles/mycatalogrole", Map.of("cat", catalogName)) + .get()) { assertThat(response).returns(Response.Status.NOT_FOUND.getStatusCode(), Response::getStatus); } // Empty list - try (Response response = managementApi.request("v1/catalogs/mycatalog1/catalog-roles").get()) { + try (Response response = + managementApi + .request("v1/catalogs/{cat}/catalog-roles", Map.of("cat", catalogName)) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1311,14 +1342,14 @@ public void testCreateListUpdateAndDeleteCatalogRole() { } // 204 Successful delete mycatalog - try (Response response = managementApi.request("v1/catalogs/mycatalog1").delete()) { - + try (Response response = + managementApi.request("v1/catalogs/{cat}", Map.of("cat", catalogName)).delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } // 204 Successful delete mycatalog2 - try (Response response = managementApi.request("v1/catalogs/mycatalog2").delete()) { - + try (Response response = + managementApi.request("v1/catalogs/{cat}", Map.of("cat", catalogName2)).delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } } @@ -1326,7 +1357,7 @@ public void testCreateListUpdateAndDeleteCatalogRole() { @Test public void testAssignListAndRevokePrincipalRoles() { // Create two Principals - Principal principal1 = new Principal("myprincipal1"); + Principal principal1 = new Principal(client.newEntityName("myprincipal1")); try (Response response = managementApi .request("v1/principals") @@ -1335,7 +1366,7 @@ public void testAssignListAndRevokePrincipalRoles() { assertThat(response).returns(Response.Status.CREATED.getStatusCode(), Response::getStatus); } - Principal principal2 = new Principal("myprincipal2"); + Principal principal2 = new Principal(client.newEntityName("myprincipal2")); try (Response response = managementApi .request("v1/principals") @@ -1345,21 +1376,24 @@ public void testAssignListAndRevokePrincipalRoles() { } // One PrincipalRole - PrincipalRole principalRole = new PrincipalRole("myprincipalrole"); + PrincipalRole principalRole = new PrincipalRole(client.newEntityName("myprincipalrole")); managementApi.createPrincipalRole(principalRole); // Assign the role to myprincipal1 try (Response response = managementApi - .request("v1/principals/myprincipal1/principal-roles") + .request( + "v1/principals/{prince}/principal-roles", Map.of("prince", principal1.getName())) .put(Entity.json(principalRole))) { - assertThat(response).returns(Response.Status.CREATED.getStatusCode(), Response::getStatus); } // Should list myprincipalrole try (Response response = - managementApi.request("v1/principals/myprincipal1/principal-roles").get()) { + managementApi + .request( + "v1/principals/{prince}/principal-roles", Map.of("prince", principal1.getName())) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1368,12 +1402,14 @@ public void testAssignListAndRevokePrincipalRoles() { .asInstanceOf(InstanceOfAssertFactories.list(PrincipalRole.class)) .hasSize(1) .satisfiesExactly( - pr -> assertThat(pr).returns("myprincipalrole", PrincipalRole::getName)); + pr -> assertThat(pr).returns(principalRole.getName(), PrincipalRole::getName)); } // Should list myprincipal1 if listing assignees of myprincipalrole try (Response response = - managementApi.request("v1/principal-roles/myprincipalrole/principals").get()) { + managementApi + .request("v1/principal-roles/{pr}/principals", Map.of("pr", principalRole.getName())) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1381,12 +1417,15 @@ public void testAssignListAndRevokePrincipalRoles() { .extracting(Principals::getPrincipals) .asInstanceOf(InstanceOfAssertFactories.list(Principal.class)) .hasSize(1) - .satisfiesExactly(pr -> assertThat(pr).returns("myprincipal1", Principal::getName)); + .satisfiesExactly(pr -> assertThat(pr).returns(principal1.getName(), Principal::getName)); } // Empty list if listing in principal2 try (Response response = - managementApi.request("v1/principals/myprincipal2/principal-roles").get()) { + managementApi + .request( + "v1/principals/{prince}/principal-roles", Map.of("prince", principal2.getName())) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1397,7 +1436,9 @@ public void testAssignListAndRevokePrincipalRoles() { // 204 Successful revoke try (Response response = managementApi - .request("v1/principals/myprincipal1/principal-roles/myprincipalrole") + .request( + "v1/principals/{prince}/principal-roles/{pr}", + Map.of("prince", principal1.getName(), "pr", principalRole.getName())) .delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); @@ -1405,7 +1446,10 @@ public void testAssignListAndRevokePrincipalRoles() { // Empty list try (Response response = - managementApi.request("v1/principals/myprincipal1/principal-roles").get()) { + managementApi + .request( + "v1/principals/{prince}/principal-roles", Map.of("prince", principal1.getName())) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) @@ -1413,8 +1457,9 @@ public void testAssignListAndRevokePrincipalRoles() { .returns(List.of(), PrincipalRoles::getRoles); } try (Response response = - managementApi.request("v1/principal-roles/myprincipalrole/principals").get()) { - + managementApi + .request("v1/principal-roles/{pr}/principals", Map.of("pr", principalRole.getName())) + .get()) { assertThat(response) .returns(Response.Status.OK.getStatusCode(), Response::getStatus) .extracting(r -> r.readEntity(Principals.class)) @@ -1422,20 +1467,27 @@ public void testAssignListAndRevokePrincipalRoles() { } // 204 Successful delete myprincipal1 - try (Response response = managementApi.request("v1/principals/myprincipal1").delete()) { - + try (Response response = + managementApi + .request("v1/principals/{prince}", Map.of("prince", principal1.getName())) + .delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } // 204 Successful delete myprincipal2 - try (Response response = managementApi.request("v1/principals/myprincipal2").delete()) { + try (Response response = + managementApi + .request("v1/principals/{prince}", Map.of("prince", principal2.getName())) + .delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } // 204 Successful delete myprincipalrole - try (Response response = managementApi.request("v1/principal-roles/myprincipalrole").delete()) { - + try (Response response = + managementApi + .request("v1/principal-roles/{pr}", Map.of("pr", principalRole.getName())) + .delete()) { assertThat(response).returns(Response.Status.NO_CONTENT.getStatusCode(), Response::getStatus); } } @@ -1611,10 +1663,10 @@ public void testAssignListAndRevokeCatalogRoles() { public void testCatalogAdminGrantAndRevokeCatalogRoles() { // Create a PrincipalRole and a new catalog. Grant the catalog_admin role to the new principal // role - String principalRoleName = "mypr33"; + String principalRoleName = client.newEntityName("mypr33"); managementApi.createPrincipalRole(principalRoleName); - String catalogName = "myuniquetestcatalog"; + String catalogName = client.newEntityName("myuniquetestcatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) @@ -1629,7 +1681,8 @@ public void testCatalogAdminGrantAndRevokeCatalogRoles() { CatalogRole catalogAdminRole = managementApi.getCatalogRole(catalogName, "catalog_admin"); managementApi.grantCatalogRoleToPrincipalRole(principalRoleName, catalogName, catalogAdminRole); - PrincipalWithCredentials catalogAdminPrincipal = managementApi.createPrincipal("principal1"); + PrincipalWithCredentials catalogAdminPrincipal = + managementApi.createPrincipal(client.newEntityName("principal1")); managementApi.assignPrincipalRole( catalogAdminPrincipal.getPrincipal().getName(), principalRoleName); @@ -1715,7 +1768,8 @@ public void testServiceAdminCanTransferCatalogAdmin() { CatalogRole catalogAdminRole = managementApi.getCatalogRole(catalogName, "catalog_admin"); managementApi.grantCatalogRoleToPrincipalRole(principalRoleName, catalogName, catalogAdminRole); - PrincipalWithCredentials catalogAdminPrincipal = managementApi.createPrincipal("principal1"); + PrincipalWithCredentials catalogAdminPrincipal = + managementApi.createPrincipal(client.newEntityName("principal1")); managementApi.assignPrincipalRole( catalogAdminPrincipal.getPrincipal().getName(), principalRole1.getName()); @@ -1766,7 +1820,7 @@ public void testCatalogAdminGrantAndRevokeCatalogRolesFromWrongCatalog() { managementApi.createPrincipalRole(principalRole1); // create a catalog - String catalogName = "mytestcatalog"; + String catalogName = client.newEntityName("mytestcatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) @@ -1800,7 +1854,8 @@ public void testCatalogAdminGrantAndRevokeCatalogRolesFromWrongCatalog() { managementApi.grantCatalogRoleToPrincipalRole(principalRoleName, catalogName, catalogAdminRole); // Create a principal and grant the principal role to it - PrincipalWithCredentials catalogAdminPrincipal = managementApi.createPrincipal("principal1"); + PrincipalWithCredentials catalogAdminPrincipal = + managementApi.createPrincipal(client.newEntityName("principal1")); managementApi.assignPrincipalRole( catalogAdminPrincipal.getPrincipal().getName(), principalRole1.getName()); @@ -1830,7 +1885,7 @@ public void testTableManageAccessCanGrantAndRevokeFromCatalogRoles() { managementApi.createPrincipalRole(principalRole1); // create a catalog - String catalogName = "mytablemanagecatalog"; + String catalogName = client.newEntityName("mytablemanagecatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) @@ -1884,7 +1939,7 @@ public void testTableManageAccessCanGrantAndRevokeFromCatalogRoles() { // Create a principal and grant the principal role to it PrincipalWithCredentials catalogAdminPrincipal = - managementApi.createPrincipal("ns_manage_access_user"); + managementApi.createPrincipal(client.newEntityName("ns_manage_access_user")); managementApi.assignPrincipalRole( catalogAdminPrincipal.getPrincipal().getName(), principalRole1.getName()); @@ -2020,7 +2075,7 @@ public void testTokenInvalidPrincipalId() { @Test public void testNamespaceExistsStatus() { // create a catalog - String catalogName = "mytablemanagecatalog"; + String catalogName = client.newEntityName("mytablemanagecatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) @@ -2046,7 +2101,7 @@ public void testNamespaceExistsStatus() { @Test public void testDropNamespaceStatus() { // create a catalog - String catalogName = "mytablemanagecatalog"; + String catalogName = client.newEntityName("mytablemanagecatalog"); Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java index 7413cc8bdc..b202f1a85f 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java @@ -144,8 +144,8 @@ static void setup(PolarisApiEndpoints apiEndpoints, ClientCredentials credential endpoints = apiEndpoints; client = polarisClient(endpoints); managementApi = client.managementApi(credentials); - String principalName = "snowman-rest-" + UUID.randomUUID(); - principalRoleName = "rest-admin-" + UUID.randomUUID(); + String principalName = client.newEntityName("snowman-rest"); + principalRoleName = client.newEntityName("rest-admin"); principalCredentials = managementApi.createPrincipalWithRole(principalName, principalRoleName); catalogApi = client.catalogApi(principalCredentials); } diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java index c7b1fcbe29..6516eb00c1 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java @@ -65,8 +65,8 @@ static void setup(PolarisApiEndpoints apiEndpoints, ClientCredentials credential endpoints = apiEndpoints; client = polarisClient(endpoints); managementApi = client.managementApi(credentials); - String principalName = "snowman-rest-" + UUID.randomUUID(); - principalRoleName = "rest-admin-" + UUID.randomUUID(); + String principalName = client.newEntityName("snowman-rest"); + principalRoleName = client.newEntityName("rest-admin"); principalCredentials = managementApi.createPrincipalWithRole(principalName, principalRoleName); } diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java index a5bc28c6b4..dc7c45ac05 100644 --- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java +++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisSparkIntegrationTest.java @@ -59,8 +59,6 @@ @ExtendWith(PolarisIntegrationTestExtension.class) public class PolarisSparkIntegrationTest { - public static final String CATALOG_NAME = "mycatalog"; - public static final String EXTERNAL_CATALOG_NAME = "external_catalog"; private static final S3MockContainer s3Container = new S3MockContainer("3.11.0").withInitialBuckets("my-bucket,my-old-bucket"); private static SparkSession spark; @@ -69,6 +67,8 @@ public class PolarisSparkIntegrationTest { private ManagementApi managementApi; private CatalogApi catalogApi; private String sparkToken; + private String catalogName; + private String externalCatalogName; @BeforeAll public static void setup() throws IOException { @@ -88,6 +88,9 @@ public void before(PolarisApiEndpoints apiEndpoints, ClientCredentials credentia managementApi = client.managementApi(credentials); catalogApi = client.catalogApi(credentials); + catalogName = client.newEntityName("spark_catalog"); + externalCatalogName = client.newEntityName("spark_ext_catalog"); + AwsStorageConfigInfo awsConfigModel = AwsStorageConfigInfo.builder() .setRoleArn("arn:aws:iam::123456789012:role/my-role") @@ -118,7 +121,7 @@ public void before(PolarisApiEndpoints apiEndpoints, ClientCredentials credentia Catalog catalog = PolarisCatalog.builder() .setType(Catalog.TypeEnum.INTERNAL) - .setName(CATALOG_NAME) + .setName(catalogName) .setProperties(props) .setStorageConfigInfo(awsConfigModel) .build(); @@ -147,7 +150,7 @@ public void before(PolarisApiEndpoints apiEndpoints, ClientCredentials credentia Catalog externalCatalog = ExternalCatalog.builder() .setType(Catalog.TypeEnum.EXTERNAL) - .setName(EXTERNAL_CATALOG_NAME) + .setName(externalCatalogName) .setProperties(externalProps) .setStorageConfigInfo(awsConfigModel) .setRemoteUrl("http://dummy_url") @@ -170,9 +173,9 @@ public void before(PolarisApiEndpoints apiEndpoints, ClientCredentials credentia .config("spark.ui.showConsoleProgress", false) .config("spark.ui.enabled", "false"); spark = - withCatalog(withCatalog(sessionBuilder, CATALOG_NAME), EXTERNAL_CATALOG_NAME).getOrCreate(); + withCatalog(withCatalog(sessionBuilder, catalogName), externalCatalogName).getOrCreate(); - onSpark("USE " + CATALOG_NAME); + onSpark("USE " + catalogName); } private SparkSession.Builder withCatalog(SparkSession.Builder builder, String catalogName) { @@ -196,8 +199,8 @@ private SparkSession.Builder withCatalog(SparkSession.Builder builder, String ca @AfterEach public void after() throws Exception { - cleanupCatalog(CATALOG_NAME); - cleanupCatalog(EXTERNAL_CATALOG_NAME); + cleanupCatalog(catalogName); + cleanupCatalog(externalCatalogName); try { SparkSession.clearDefaultSession(); SparkSession.clearActiveSession(); @@ -252,7 +255,7 @@ public void testCreateAndUpdateExternalTable() { long recordCount = onSpark("SELECT * FROM tb1").count(); assertThat(recordCount).isEqualTo(3); - onSpark("USE " + EXTERNAL_CATALOG_NAME); + onSpark("USE " + externalCatalogName); List existingNamespaces = onSpark("SHOW NAMESPACES").collectAsList(); assertThat(existingNamespaces).isEmpty(); @@ -261,11 +264,10 @@ public void testCreateAndUpdateExternalTable() { List existingTables = onSpark("SHOW TABLES").collectAsList(); assertThat(existingTables).isEmpty(); - LoadTableResponse tableResponse = loadTable(CATALOG_NAME, "ns1", "tb1"); + LoadTableResponse tableResponse = loadTable(catalogName, "ns1", "tb1"); try (Response registerResponse = catalogApi - .request( - "v1/{cat}/namespaces/externalns1/register", Map.of("cat", EXTERNAL_CATALOG_NAME)) + .request("v1/{cat}/namespaces/externalns1/register", Map.of("cat", externalCatalogName)) .post( Entity.json( ImmutableRegisterTableRequest.builder() @@ -284,8 +286,8 @@ public void testCreateAndUpdateExternalTable() { assertThatThrownBy(() -> onSpark("INSERT INTO mytb1 VALUES (20, 'new_text')")) .isInstanceOf(Exception.class); - onSpark("INSERT INTO " + CATALOG_NAME + ".ns1.tb1 VALUES (20, 'new_text')"); - tableResponse = loadTable(CATALOG_NAME, "ns1", "tb1"); + onSpark("INSERT INTO " + catalogName + ".ns1.tb1 VALUES (20, 'new_text')"); + tableResponse = loadTable(catalogName, "ns1", "tb1"); Map updateNotification = ImmutableMap.builder() .put("table-name", "mytb1") @@ -303,7 +305,7 @@ public void testCreateAndUpdateExternalTable() { catalogApi .request( "v1/{cat}/namespaces/externalns1/tables/mytb1/notifications", - Map.of("cat", EXTERNAL_CATALOG_NAME)) + Map.of("cat", externalCatalogName)) .post(Entity.json(notificationRequest))) { assertThat(notifyResponse) .extracting(Response::getStatus)