This is an automated email from the ASF dual-hosted git repository.
sarathsubramanian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/master by this push:
new d96d3c6c6 ATLAS-5337: Fix Trino Extractor Jersey client failures for
AtlasEntityWithExtInfo POST/GET (#690)
d96d3c6c6 is described below
commit d96d3c6c6a53e20f2f32c507ff0198bf356f48cb
Author: Ramachandran Krishnan <[email protected]>
AuthorDate: Mon Jul 6 20:16:57 2026 +0530
ATLAS-5337: Fix Trino Extractor Jersey client failures for
AtlasEntityWithExtInfo POST/GET (#690)
* ATLAS-5337: Fix Trino Extractor Jersey client failures for
AtlasEntityWithExtInfo
Remove jersey-client 1.9 pin from trino-extractor; JSON-serialize entity
POSTs
via AtlasType.toJson() in AtlasClientV2; parse model GET responses with
AtlasJson in AtlasBaseClient; support ATLAS_USERNAME/ATLAS_PASSWORD env vars
in AuthenticationUtil for non-interactive runs.
* Fix flaky QuickStart IT assertions for non-deterministic list order.
QuickStartIT and QuickStartV2IT assumed fixed column/process input ordering
from graph traversal, which fails intermittently on Java 17 CI.
* CI: retrigger workflow after GlossaryServiceTest flake.
* CI: retrigger workflow after atlas-hbase docker flake.
* Fix CI flakes in GlossaryServiceTest, BasicSearchIT, and atlas-hbase
docker.
Retry transient JanusGraph errors during glossary test setup, scope
BasicSearchIT
hive queries to the imported @cl1 dataset, and harden atlas-hbase container
startup
and health checks so docker compose --wait does not fail spuriously.
---------
Co-authored-by: ramk <[email protected]>
---
.github/workflows/ci.yml | 3 +
addons/trino-extractor/pom.xml | 5 --
.../main/java/org/apache/atlas/AtlasClientV2.java | 10 +--
.../java/org/apache/atlas/AtlasBaseClient.java | 12 ++++
.../atlas-docker/docker-compose.atlas-hbase.yml | 3 +-
dev-support/atlas-docker/scripts/atlas-hbase.sh | 16 ++++-
.../org/apache/atlas/utils/AuthenticationUtil.java | 6 ++
.../apache/atlas/glossary/GlossaryServiceTest.java | 75 +++++++++++++++++++---
.../org/apache/atlas/examples/QuickStartIT.java | 19 ++++--
.../org/apache/atlas/examples/QuickStartV2IT.java | 12 +++-
.../atlas/web/integration/BasicSearchIT.java | 43 +++++++++++--
11 files changed, 170 insertions(+), 34 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 721c039c3..ededed192 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -145,6 +145,9 @@ jobs:
echo "All required containers are up and running";
docker stop $(docker ps -q) && docker rm $(docker ps -aq);
else
+ for container in "${containers[@]}"; do
+ docker logs --tail 200 "$container" 2>&1 || true
+ done
docker stop $(docker ps -q) && docker rm $(docker ps -aq);
exit 1;
fi
diff --git a/addons/trino-extractor/pom.xml b/addons/trino-extractor/pom.xml
index a7a66b937..b66b957ca 100644
--- a/addons/trino-extractor/pom.xml
+++ b/addons/trino-extractor/pom.xml
@@ -42,11 +42,6 @@
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-client</artifactId>
- <version>1.9</version>
- </dependency>
<dependency>
<groupId>io.trino</groupId>
diff --git a/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
index a46a6724a..daa600949 100644
--- a/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
+++ b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
@@ -457,25 +457,25 @@ public class AtlasClientV2 extends AtlasBaseClient {
}
public EntityMutationResponse createEntity(AtlasEntityWithExtInfo entity)
throws AtlasServiceException {
- return callAPI(API_V2.CREATE_ENTITY, EntityMutationResponse.class,
entity);
+ return callAPI(API_V2.CREATE_ENTITY, EntityMutationResponse.class,
AtlasType.toJson(entity));
}
public EntityMutationResponse createEntities(AtlasEntitiesWithExtInfo
atlasEntities) throws AtlasServiceException {
- return callAPI(API_V2.CREATE_ENTITIES, EntityMutationResponse.class,
atlasEntities);
+ return callAPI(API_V2.CREATE_ENTITIES, EntityMutationResponse.class,
AtlasType.toJson(atlasEntities));
}
public EntityMutationResponse updateEntity(AtlasEntityWithExtInfo entity)
throws AtlasServiceException {
- return callAPI(API_V2.UPDATE_ENTITY, EntityMutationResponse.class,
entity);
+ return callAPI(API_V2.UPDATE_ENTITY, EntityMutationResponse.class,
AtlasType.toJson(entity));
}
public EntityMutationResponse updateEntities(AtlasEntitiesWithExtInfo
atlasEntities) throws AtlasServiceException {
- return callAPI(API_V2.UPDATE_ENTITIES, EntityMutationResponse.class,
atlasEntities);
+ return callAPI(API_V2.UPDATE_ENTITIES, EntityMutationResponse.class,
AtlasType.toJson(atlasEntities));
}
public EntityMutationResponse updateEntityByAttribute(String typeName,
Map<String, String> uniqAttributes, AtlasEntityWithExtInfo entityInfo) throws
AtlasServiceException {
MultivaluedMap<String, String> queryParams =
attributesToQueryParams(uniqAttributes);
- return callAPI(API_V2.UPDATE_ENTITY_BY_ATTRIBUTE,
EntityMutationResponse.class, entityInfo, queryParams, typeName);
+ return callAPI(API_V2.UPDATE_ENTITY_BY_ATTRIBUTE,
EntityMutationResponse.class, AtlasType.toJson(entityInfo), queryParams,
typeName);
}
public EntityMutationResponse partialUpdateEntityByGuid(String entityGuid,
Object attrValue, String attrName) throws AtlasServiceException {
diff --git a/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
b/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
index b6371d32f..9b62e82d9 100644
--- a/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
+++ b/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
@@ -496,6 +496,14 @@ public abstract class AtlasBaseClient {
} catch (IOException e) {
throw new AtlasServiceException(api, e);
}
+ } else if (isAtlasModelType(responseType.getRawClass())) {
+ String stringEntity =
clientResponse.getEntity(String.class);
+ T entity = AtlasJson.fromJson(stringEntity,
responseType.getRawClass());
+
+ LOG.debug("Response : {}", entity);
+
LOG.debug("------------------------------------------------------");
+
+ return entity;
} else {
T entity = clientResponse.getEntity(responseType);
@@ -657,6 +665,10 @@ public abstract class AtlasBaseClient {
this.configuration = configuration;
}
+ private static boolean isAtlasModelType(Class<?> clazz) {
+ return clazz != null &&
clazz.getName().startsWith("org.apache.atlas.model.");
+ }
+
@VisibleForTesting
void setService(WebResource resource) {
this.service = resource;
diff --git a/dev-support/atlas-docker/docker-compose.atlas-hbase.yml
b/dev-support/atlas-docker/docker-compose.atlas-hbase.yml
index 0e2d5f9da..9d46702c2 100644
--- a/dev-support/atlas-docker/docker-compose.atlas-hbase.yml
+++ b/dev-support/atlas-docker/docker-compose.atlas-hbase.yml
@@ -25,8 +25,9 @@ services:
condition: service_started
atlas-kafka:
condition: service_started
+ restart: unless-stopped
healthcheck:
- test: [ "CMD", "wget", "-q", "--spider",
"http://localhost:16030/rs-status" ]
+ test: [ "CMD-SHELL", "wget -q --spider http://localhost:16030/rs-status
&& wget -q --spider http://localhost:16010/master-status" ]
interval: 30s
timeout: 10s
retries: 30
diff --git a/dev-support/atlas-docker/scripts/atlas-hbase.sh
b/dev-support/atlas-docker/scripts/atlas-hbase.sh
index 81b382b5d..e139a9377 100755
--- a/dev-support/atlas-docker/scripts/atlas-hbase.sh
+++ b/dev-support/atlas-docker/scripts/atlas-hbase.sh
@@ -33,7 +33,21 @@ fi
su -c "${HBASE_HOME}/bin/start-hbase.sh" hbase
-HBASE_MASTER_PID=`ps -ef | grep -v grep | grep -i
"org.apache.hadoop.hbase.master.HMaster" | awk '{print $2}'`
+HBASE_MASTER_PID=""
+for attempt in $(seq 1 60); do
+ HBASE_MASTER_PID=`ps -ef | grep -v grep | grep -i
"org.apache.hadoop.hbase.master.HMaster" | awk '{print $2}'`
+
+ if [ -n "${HBASE_MASTER_PID}" ]; then
+ break
+ fi
+
+ sleep 2
+done
+
+if [ -z "${HBASE_MASTER_PID}" ]; then
+ echo "HBase HMaster failed to start" >&2
+ exit 1
+fi
# prevent the container from exiting
tail --pid=$HBASE_MASTER_PID -f /dev/null
diff --git a/intg/src/main/java/org/apache/atlas/utils/AuthenticationUtil.java
b/intg/src/main/java/org/apache/atlas/utils/AuthenticationUtil.java
index d98de2812..229f17786 100644
--- a/intg/src/main/java/org/apache/atlas/utils/AuthenticationUtil.java
+++ b/intg/src/main/java/org/apache/atlas/utils/AuthenticationUtil.java
@@ -74,6 +74,12 @@ public final class AuthenticationUtil {
}
public static String[] getBasicAuthenticationInput() {
+ String envUser = System.getenv("ATLAS_USERNAME");
+ String envPass = System.getenv("ATLAS_PASSWORD");
+ if (envUser != null && !envUser.isEmpty() && envPass != null) {
+ return new String[] {envUser, envPass};
+ }
+
String username = null;
String password = null;
diff --git
a/repository/src/test/java/org/apache/atlas/glossary/GlossaryServiceTest.java
b/repository/src/test/java/org/apache/atlas/glossary/GlossaryServiceTest.java
index be32b0344..f8cac1c3c 100644
---
a/repository/src/test/java/org/apache/atlas/glossary/GlossaryServiceTest.java
+++
b/repository/src/test/java/org/apache/atlas/glossary/GlossaryServiceTest.java
@@ -152,20 +152,15 @@ public class GlossaryServiceTest {
@BeforeClass
public void setupSampleGlossary() {
try {
- TestLoadModelUtils.loadAllModels("0000-Area0", typeDefStore,
typeRegistry);
+ loadAllModelsWithRetry("0000-Area0");
} catch (AtlasBaseException | IOException e) {
- throw new SkipException("SubjectArea model loading failed");
+ throw new SkipException("SubjectArea model loading failed: " +
e.getMessage());
}
try {
- AtlasClassificationDef classificationDef = new
AtlasClassificationDef("TestClassification", "Test only classification");
- AtlasTypesDef typesDef = new AtlasTypesDef();
-
-
typesDef.setClassificationDefs(Collections.singletonList(classificationDef));
-
- typeDefStore.createTypesDef(typesDef);
+ createTestClassificationWithRetry();
} catch (AtlasBaseException e) {
- throw new SkipException("Test classification creation failed");
+ throw new SkipException("Test classification creation failed: " +
e.getMessage());
}
// Glossary
@@ -1416,6 +1411,68 @@ public class GlossaryServiceTest {
}
}
+ private void loadAllModelsWithRetry(String dirName) throws
AtlasBaseException, IOException {
+ int maxAttempts = 5;
+
+ for (int attempt = 1; attempt <= maxAttempts; attempt++) {
+ try {
+ TestLoadModelUtils.loadAllModels(dirName, typeDefStore,
typeRegistry);
+ return;
+ } catch (AtlasBaseException e) {
+ if (attempt == maxAttempts || !isTransientGraphError(e)) {
+ throw e;
+ }
+
+ LOG.warn("loadAllModels failed (attempt {}/{}), retrying: {}",
attempt, maxAttempts, e.getMessage());
+ sleepBeforeRetry(attempt);
+ }
+ }
+ }
+
+ private void createTestClassificationWithRetry() throws AtlasBaseException
{
+ AtlasClassificationDef classificationDef = new
AtlasClassificationDef("TestClassification", "Test only classification");
+ AtlasTypesDef typesDef = new AtlasTypesDef();
+
+
typesDef.setClassificationDefs(Collections.singletonList(classificationDef));
+
+ int maxAttempts = 5;
+
+ for (int attempt = 1; attempt <= maxAttempts; attempt++) {
+ try {
+ typeDefStore.createTypesDef(typesDef);
+ return;
+ } catch (AtlasBaseException e) {
+ if (attempt == maxAttempts || !isTransientGraphError(e)) {
+ throw e;
+ }
+
+ LOG.warn("createTypesDef failed (attempt {}/{}), retrying:
{}", attempt, maxAttempts, e.getMessage());
+ sleepBeforeRetry(attempt);
+ }
+ }
+ }
+
+ private static boolean isTransientGraphError(AtlasBaseException e) {
+ if (e == null || e.getMessage() == null) {
+ return false;
+ }
+
+ String message = e.getMessage().toLowerCase();
+
+ return message.contains("could not start new transaction")
+ || message.contains("cursor has been closed")
+ || message.contains("permanentlockingexception");
+ }
+
+ private static void sleepBeforeRetry(int attempt) throws
AtlasBaseException {
+ try {
+ Thread.sleep(500L * attempt);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new AtlasBaseException(AtlasErrorCode.INTERNAL_ERROR, ie);
+ }
+ }
+
private static InputStream getFile(String subDir, String fileName) {
final String userDir = System.getProperty("user.dir");
String filePath = getTestFilePath(userDir, subDir, fileName);
diff --git a/webapp/src/test/java/org/apache/atlas/examples/QuickStartIT.java
b/webapp/src/test/java/org/apache/atlas/examples/QuickStartIT.java
index 707add619..1c0b2fb6c 100644
--- a/webapp/src/test/java/org/apache/atlas/examples/QuickStartIT.java
+++ b/webapp/src/test/java/org/apache/atlas/examples/QuickStartIT.java
@@ -30,6 +30,8 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -80,9 +82,11 @@ public class QuickStartIT extends BaseResourceIT {
String timeDimTableId = getTableId(QuickStart.TIME_DIM_TABLE);
String salesFactDailyMVId =
getTableId(QuickStart.SALES_FACT_DAILY_MV_TABLE);
- assertEquals(salesFactTableId, inputs.get(0)._getId());
- assertEquals(timeDimTableId, inputs.get(1)._getId());
- assertEquals(salesFactDailyMVId, outputs.get(0)._getId());
+ Set<String> inputIds =
inputs.stream().map(Id::_getId).collect(Collectors.toSet());
+
+ assertTrue(inputIds.contains(salesFactTableId));
+ assertTrue(inputIds.contains(timeDimTableId));
+ assertEquals(outputs.get(0)._getId(), salesFactDailyMVId);
}
@Test
@@ -138,10 +142,13 @@ public class QuickStartIT extends BaseResourceIT {
assertEquals(columns.size(), 4);
- Referenceable column = columns.get(0);
+ Referenceable timeIdColumn = columns.stream()
+ .filter(c -> QuickStart.TIME_ID_COLUMN.equals(c.get("name")))
+ .findFirst()
+ .orElse(null);
- assertEquals(column.get("name"), QuickStart.TIME_ID_COLUMN);
- assertEquals(column.get("dataType"), "int");
+ assertNotNull(timeIdColumn);
+ assertEquals(timeIdColumn.get("dataType"), "int");
}
private void verifyDBIsLinkedToTable(Referenceable table) throws
AtlasServiceException {
diff --git a/webapp/src/test/java/org/apache/atlas/examples/QuickStartV2IT.java
b/webapp/src/test/java/org/apache/atlas/examples/QuickStartV2IT.java
index aaec24dbf..c020ccc2c 100644
--- a/webapp/src/test/java/org/apache/atlas/examples/QuickStartV2IT.java
+++ b/webapp/src/test/java/org/apache/atlas/examples/QuickStartV2IT.java
@@ -36,7 +36,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
import static org.apache.atlas.AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME;
import static org.apache.atlas.examples.QuickStartV2.CLUSTER_SUFFIX;
@@ -119,9 +121,13 @@ public class QuickStartV2IT extends BaseResourceIT {
String timeDimTableId = getTableId(TIME_DIM_TABLE);
String salesFactDailyMVId = getTableId(SALES_FACT_DAILY_MV_TABLE);
- assertEquals(salesFactTableId, ((Map<?, ?>)
inputs.get(0)).get("guid"));
- assertEquals(timeDimTableId, ((Map<?, ?>) inputs.get(1)).get("guid"));
- assertEquals(salesFactDailyMVId, ((Map<?, ?>)
outputs.get(0)).get("guid"));
+ Set<String> inputGuids = inputs.stream()
+ .map(i -> (String) ((Map<?, ?>) i).get("guid"))
+ .collect(Collectors.toSet());
+
+ assertTrue(inputGuids.contains(salesFactTableId));
+ assertTrue(inputGuids.contains(timeDimTableId));
+ assertEquals(((Map<?, ?>) outputs.get(0)).get("guid"),
salesFactDailyMVId);
}
@Test
diff --git
a/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java
b/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java
index d814c7771..24c7b1924 100644
--- a/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java
+++ b/webapp/src/test/java/org/apache/atlas/web/integration/BasicSearchIT.java
@@ -57,6 +57,8 @@ import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class BasicSearchIT extends BaseResourceIT {
+ private static final String IMPORTED_DATA_QUALIFIED_NAME_MARKER = "@cl1";
+
private AtlasUserSavedSearch userSavedSearch;
@BeforeClass
@@ -141,14 +143,15 @@ public class BasicSearchIT extends BaseResourceIT {
LOG.info("TestDescription :{}",
testExpectation.testDescription);
LOG.info("SearchParameters :{}",
testExpectation.searchParameters);
- AtlasSearchResult searchResult =
atlasClientV2.facetedSearch(testExpectation.searchParameters);
+ SearchParameters searchParameters =
scopeToImportedDataset(testExpectation.searchParameters);
+ AtlasSearchResult searchResult =
atlasClientV2.facetedSearch(searchParameters);
if (testExpectation.expectedCount > 0) {
assertNotNull(searchResult.getEntities());
assertEquals(searchResult.getEntities().size(),
testExpectation.expectedCount);
}
- if (testExpectation.searchParameters.getSortBy() != null &&
!testExpectation.searchParameters.getSortBy().isEmpty()) {
+ if (searchParameters.getSortBy() != null &&
!searchParameters.getSortBy().isEmpty()) {
assertNotNull(searchResult.getEntities());
assertEquals(searchResult.getEntities().get(0).getAttribute("name"),
"testtable_3");
}
@@ -169,7 +172,7 @@ public class BasicSearchIT extends BaseResourceIT {
LOG.info("TestDescription :{}",
testExpectation.testDescription);
LOG.info("SearchParameters :{}",
testExpectation.searchParameters);
- SearchParameters parameters =
testExpectation.getSearchParameters();
+ SearchParameters parameters =
scopeToImportedDataset(testExpectation.getSearchParameters());
if (parameters.getEntityFilters() == null ||
parameters.getEntityFilters().getAttributeName() == null) {
continue;
@@ -204,7 +207,7 @@ public class BasicSearchIT extends BaseResourceIT {
LOG.info("TestDescription :{}",
testExpectation.testDescription);
LOG.info("SearchParameters :{}",
testExpectation.searchParameters);
- SearchParameters parameters =
testExpectation.getSearchParameters();
+ SearchParameters parameters =
scopeToImportedDataset(testExpectation.getSearchParameters());
AtlasUserSavedSearch savedSearch = new AtlasUserSavedSearch();
@@ -336,6 +339,38 @@ public class BasicSearchIT extends BaseResourceIT {
return containsAnyMatchingCriteria(filter, fc ->
StringUtils.isBlank(fc.getAttributeValue()));
}
+ private SearchParameters scopeToImportedDataset(SearchParameters
parameters) {
+ if (parameters == null || parameters.getTypeName() == null) {
+ return parameters;
+ }
+
+ String typeName = parameters.getTypeName();
+
+ if (!"hive_table".equals(typeName) && !"hive_column".equals(typeName))
{
+ return parameters;
+ }
+
+ SearchParameters.FilterCriteria importedDataFilter = new
SearchParameters.FilterCriteria();
+
+ importedDataFilter.setAttributeName("qualifiedName");
+ importedDataFilter.setOperator(SearchParameters.Operator.CONTAINS);
+
importedDataFilter.setAttributeValue(IMPORTED_DATA_QUALIFIED_NAME_MARKER);
+
+ SearchParameters.FilterCriteria scopedFilter = new
SearchParameters.FilterCriteria();
+
+
scopedFilter.setCondition(SearchParameters.FilterCriteria.Condition.AND);
+
+ if (parameters.getEntityFilters() != null) {
+ scopedFilter.setCriterion(Arrays.asList(importedDataFilter,
parameters.getEntityFilters()));
+ } else {
+
scopedFilter.setCriterion(Collections.singletonList(importedDataFilter));
+ }
+
+ parameters.setEntityFilters(scopedFilter);
+
+ return parameters;
+ }
+
@Test(dependsOnMethods = "testSavedSearch")
public void testExecuteSavedSearchByName() {
try {