Skip to content

Commit f180bee

Browse files
authored
RANGER-5522: update tagsync to process Atlas notifications for Trino entities (#895)
1 parent 63d5aab commit f180bee

3 files changed

Lines changed: 302 additions & 0 deletions

File tree

tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasResourceMapperUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public static boolean initializeAtlasResourceMappers(Properties properties) {
8686
mapperNames.add("org.apache.ranger.tagsync.source.atlas.AtlasHbaseResourceMapper");
8787
mapperNames.add("org.apache.ranger.tagsync.source.atlas.AtlasKafkaResourceMapper");
8888
mapperNames.add("org.apache.ranger.tagsync.source.atlas.AtlasOzoneResourceMapper");
89+
mapperNames.add("org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper");
8990

9091
mapperNames.add(AtlasAdlsResourceMapper.class.getName());
9192

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.ranger.tagsync.source.atlas;
21+
22+
import org.apache.commons.lang3.StringUtils;
23+
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
24+
import org.apache.ranger.plugin.model.RangerServiceResource;
25+
import org.apache.ranger.tagsync.source.atlasrest.RangerAtlasEntity;
26+
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
public class AtlasTrinoResourceMapper extends AtlasResourceMapper {
31+
public static final String ENTITY_TYPE_TRINO_CATALOG = "trino_catalog";
32+
public static final String ENTITY_TYPE_TRINO_SCHEMA = "trino_schema";
33+
public static final String ENTITY_TYPE_TRINO_TABLE = "trino_table";
34+
public static final String ENTITY_TYPE_TRINO_COLUMN = "trino_column";
35+
public static final String RANGER_TYPE_TRINO_CATALOG = "catalog";
36+
public static final String RANGER_TYPE_TRINO_SCHEMA = "schema";
37+
public static final String RANGER_TYPE_TRINO_TABLE = "table";
38+
public static final String RANGER_TYPE_TRINO_COLUMN = "column";
39+
40+
public static final String[] SUPPORTED_ENTITY_TYPES = {
41+
ENTITY_TYPE_TRINO_CATALOG,
42+
ENTITY_TYPE_TRINO_SCHEMA,
43+
ENTITY_TYPE_TRINO_TABLE,
44+
ENTITY_TYPE_TRINO_COLUMN
45+
};
46+
47+
public AtlasTrinoResourceMapper() {
48+
super("trino", SUPPORTED_ENTITY_TYPES);
49+
}
50+
51+
/*
52+
* qualifiedName can be of format, depending upon the entity-type:
53+
* trino_catalog: <catalog>@<instanceName>
54+
* trino_schema: <catalog>.<schema>@<instanceName>
55+
* trino_table: <catalog>.<schema>.<table>@<instanceName>
56+
* trino_column: <catalog>.<schema>.<table>.<column>@<instanceName>
57+
*/
58+
@Override
59+
public RangerServiceResource buildResource(RangerAtlasEntity entity) throws Exception {
60+
String qualifiedName = (String) entity.getAttributes().get(AtlasResourceMapper.ENTITY_ATTRIBUTE_QUALIFIED_NAME);
61+
62+
if (StringUtils.isEmpty(qualifiedName)) {
63+
throw new Exception("attribute '" + ENTITY_ATTRIBUTE_QUALIFIED_NAME + "' not found in entity");
64+
}
65+
66+
String entityType = entity.getTypeName();
67+
String entityGuid = entity.getGuid();
68+
String resourceStr = getResourceNameFromQualifiedName(qualifiedName);
69+
70+
if (StringUtils.isEmpty(resourceStr)) {
71+
throwExceptionWithMessage("resource not found in attribute '" + ENTITY_ATTRIBUTE_QUALIFIED_NAME + "': " + qualifiedName);
72+
}
73+
74+
String trinoInstance = getClusterNameFromQualifiedName(qualifiedName);
75+
if (StringUtils.equals(resourceStr, qualifiedName)) {
76+
trinoInstance = resourceStr;
77+
}
78+
79+
if (StringUtils.isEmpty(trinoInstance)) {
80+
throwExceptionWithMessage("trino-instance not found in attribute '" + ENTITY_ATTRIBUTE_QUALIFIED_NAME + "': " + qualifiedName);
81+
}
82+
String serviceName = getRangerServiceName(trinoInstance);
83+
84+
String[] parts = resourceStr.split(QUALIFIED_NAME_DELIMITER);
85+
if (parts.length < 1 || parts.length > 4) {
86+
throwExceptionWithMessage("invalid resource format in attribute '" + ENTITY_ATTRIBUTE_QUALIFIED_NAME + "': " + qualifiedName);
87+
}
88+
89+
Map<String, RangerPolicyResource> elements = new HashMap<>();
90+
if (StringUtils.equals(entityType, ENTITY_TYPE_TRINO_CATALOG)) {
91+
if (parts.length == 1 && StringUtils.isNotEmpty(parts[0])) {
92+
elements.put(RANGER_TYPE_TRINO_CATALOG, new RangerPolicyResource(parts[0]));
93+
}
94+
} else if (StringUtils.equals(entityType, ENTITY_TYPE_TRINO_SCHEMA)) {
95+
if (parts.length == 2 && StringUtils.isNotEmpty(parts[0]) && StringUtils.isNotEmpty(parts[1])) {
96+
elements.put(RANGER_TYPE_TRINO_CATALOG, new RangerPolicyResource(parts[0]));
97+
elements.put(RANGER_TYPE_TRINO_SCHEMA, new RangerPolicyResource(parts[1]));
98+
}
99+
} else if (StringUtils.equals(entityType, ENTITY_TYPE_TRINO_TABLE)) {
100+
if (parts.length == 3 && StringUtils.isNotEmpty(parts[0]) && StringUtils.isNotEmpty(parts[1]) && StringUtils.isNotEmpty(parts[2])) {
101+
elements.put(RANGER_TYPE_TRINO_CATALOG, new RangerPolicyResource(parts[0]));
102+
elements.put(RANGER_TYPE_TRINO_SCHEMA, new RangerPolicyResource(parts[1]));
103+
elements.put(RANGER_TYPE_TRINO_TABLE, new RangerPolicyResource(parts[2]));
104+
}
105+
} else if (StringUtils.equals(entityType, ENTITY_TYPE_TRINO_COLUMN)) {
106+
if (parts.length == 4 && StringUtils.isNotEmpty(parts[0]) && StringUtils.isNotEmpty(parts[1]) && StringUtils.isNotEmpty(parts[2]) && StringUtils.isNotEmpty(parts[3])) {
107+
elements.put(RANGER_TYPE_TRINO_CATALOG, new RangerPolicyResource(parts[0]));
108+
elements.put(RANGER_TYPE_TRINO_SCHEMA, new RangerPolicyResource(parts[1]));
109+
elements.put(RANGER_TYPE_TRINO_TABLE, new RangerPolicyResource(parts[2]));
110+
elements.put(RANGER_TYPE_TRINO_COLUMN, new RangerPolicyResource(parts[3]));
111+
}
112+
} else {
113+
throwExceptionWithMessage("unrecognized entity-type: " + entityType);
114+
}
115+
116+
if (elements.isEmpty()) {
117+
throwExceptionWithMessage("invalid qualifiedName for entity-type '" + entityType + "': " + qualifiedName);
118+
}
119+
120+
return new RangerServiceResource(entityGuid, serviceName, elements);
121+
}
122+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.ranger.tagsync.process;
21+
22+
import org.apache.ranger.plugin.model.RangerServiceResource;
23+
import org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper;
24+
import org.apache.ranger.tagsync.source.atlasrest.RangerAtlasEntity;
25+
import org.junit.jupiter.api.Assertions;
26+
import org.junit.jupiter.api.Test;
27+
28+
import java.util.Collections;
29+
30+
import static org.apache.ranger.tagsync.source.atlas.AtlasResourceMapper.ENTITY_ATTRIBUTE_QUALIFIED_NAME;
31+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.ENTITY_TYPE_TRINO_CATALOG;
32+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.ENTITY_TYPE_TRINO_COLUMN;
33+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.ENTITY_TYPE_TRINO_SCHEMA;
34+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.ENTITY_TYPE_TRINO_TABLE;
35+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.RANGER_TYPE_TRINO_CATALOG;
36+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.RANGER_TYPE_TRINO_COLUMN;
37+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.RANGER_TYPE_TRINO_SCHEMA;
38+
import static org.apache.ranger.tagsync.source.atlas.AtlasTrinoResourceMapper.RANGER_TYPE_TRINO_TABLE;
39+
40+
public class TestTrinoResourceMapper {
41+
private static final String CATALOG_QUALIFIED_NAME = "sales@dev";
42+
private static final String SCHEMA_QUALIFIED_NAME = "sales.reporting@dev";
43+
private static final String TABLE_QUALIFIED_NAME = "sales.reporting.orders@dev";
44+
private static final String COLUMN_QUALIFIED_NAME = "sales.reporting.orders.customer_id@dev";
45+
private static final String INVALID_RESOURCE_QUALIFIED_NAME = "sales.reporting.orders.customer_id.extra@dev";
46+
47+
private static final String SERVICE_NAME = "dev_trino";
48+
private static final String RANGER_CATALOG = "sales";
49+
private static final String RANGER_SCHEMA = "reporting";
50+
private static final String RANGER_TABLE = "orders";
51+
private static final String RANGER_COLUMN = "customer_id";
52+
53+
AtlasTrinoResourceMapper resourceMapper = new AtlasTrinoResourceMapper();
54+
55+
@Test
56+
public void testTrinoCatalog() throws Exception {
57+
RangerAtlasEntity entity = getEntity(ENTITY_TYPE_TRINO_CATALOG, CATALOG_QUALIFIED_NAME);
58+
RangerServiceResource resource = resourceMapper.buildResource(entity);
59+
60+
Assertions.assertEquals(SERVICE_NAME, resource.getServiceName());
61+
assertCatalogResource(resource);
62+
}
63+
64+
@Test
65+
public void testTrinoSchema() throws Exception {
66+
RangerAtlasEntity entity = getEntity(ENTITY_TYPE_TRINO_SCHEMA, SCHEMA_QUALIFIED_NAME);
67+
RangerServiceResource resource = resourceMapper.buildResource(entity);
68+
69+
Assertions.assertEquals(SERVICE_NAME, resource.getServiceName());
70+
assertSchemaResource(resource);
71+
}
72+
73+
@Test
74+
public void testTrinoTable() throws Exception {
75+
RangerAtlasEntity entity = getEntity(ENTITY_TYPE_TRINO_TABLE, TABLE_QUALIFIED_NAME);
76+
RangerServiceResource resource = resourceMapper.buildResource(entity);
77+
78+
Assertions.assertEquals(SERVICE_NAME, resource.getServiceName());
79+
assertTableResource(resource);
80+
}
81+
82+
@Test
83+
public void testTrinoColumn() throws Exception {
84+
RangerAtlasEntity entity = getEntity(ENTITY_TYPE_TRINO_COLUMN, COLUMN_QUALIFIED_NAME);
85+
RangerServiceResource resource = resourceMapper.buildResource(entity);
86+
87+
Assertions.assertEquals(SERVICE_NAME, resource.getServiceName());
88+
assertColumnResource(resource);
89+
}
90+
91+
@Test
92+
public void testInvalidCatalogEntity() {
93+
assertException(getEntity(ENTITY_TYPE_TRINO_CATALOG, null), "attribute 'qualifiedName' not found");
94+
assertException(getEntity(ENTITY_TYPE_TRINO_CATALOG, ""), "attribute 'qualifiedName' not found");
95+
}
96+
97+
@Test
98+
public void testInvalidSchemaEntity() {
99+
assertException(getEntity(ENTITY_TYPE_TRINO_SCHEMA, null), "attribute 'qualifiedName' not found");
100+
assertException(getEntity(ENTITY_TYPE_TRINO_SCHEMA, ""), "attribute 'qualifiedName' not found");
101+
assertException(getEntity(ENTITY_TYPE_TRINO_SCHEMA, CATALOG_QUALIFIED_NAME), "invalid qualifiedName");
102+
}
103+
104+
@Test
105+
public void testInvalidTableEntity() throws Exception {
106+
assertException(getEntity(ENTITY_TYPE_TRINO_TABLE, null), "attribute 'qualifiedName' not found");
107+
assertException(getEntity(ENTITY_TYPE_TRINO_TABLE, ""), "attribute 'qualifiedName' not found");
108+
assertException(getEntity(ENTITY_TYPE_TRINO_TABLE, SCHEMA_QUALIFIED_NAME), "invalid qualifiedName");
109+
110+
RangerServiceResource resource = resourceMapper.buildResource(getEntity(ENTITY_TYPE_TRINO_TABLE, "sales.reporting.orders"));
111+
Assertions.assertNotEquals(1, resource.getResourceElements().size());
112+
}
113+
114+
@Test
115+
public void testInvalidColumnEntity() throws Exception {
116+
assertException(getEntity(ENTITY_TYPE_TRINO_COLUMN, null), "attribute 'qualifiedName' not found");
117+
assertException(getEntity(ENTITY_TYPE_TRINO_COLUMN, ""), "attribute 'qualifiedName' not found");
118+
assertException(getEntity(ENTITY_TYPE_TRINO_COLUMN, TABLE_QUALIFIED_NAME), "invalid qualifiedName");
119+
120+
RangerServiceResource resource = resourceMapper.buildResource(getEntity(ENTITY_TYPE_TRINO_COLUMN, "sales.reporting.orders.customer_id"));
121+
Assertions.assertNotEquals(1, resource.getResourceElements().size());
122+
123+
assertException(getEntity(ENTITY_TYPE_TRINO_COLUMN, INVALID_RESOURCE_QUALIFIED_NAME), "invalid resource format");
124+
}
125+
126+
private RangerAtlasEntity getEntity(String entityType, String qualifiedName) {
127+
return new RangerAtlasEntity(entityType, "guid-" + entityType, Collections.singletonMap(ENTITY_ATTRIBUTE_QUALIFIED_NAME, qualifiedName));
128+
}
129+
130+
private void assertResourceElementCount(RangerServiceResource resource, int count) {
131+
Assertions.assertNotNull(resource);
132+
Assertions.assertEquals(SERVICE_NAME, resource.getServiceName());
133+
Assertions.assertNotNull(resource.getResourceElements());
134+
Assertions.assertEquals(count, resource.getResourceElements().size());
135+
}
136+
137+
private void assertCatalogResource(RangerServiceResource resource) {
138+
assertResourceElementCount(resource, 1);
139+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_CATALOG, RANGER_CATALOG);
140+
}
141+
142+
private void assertSchemaResource(RangerServiceResource resource) {
143+
assertResourceElementCount(resource, 2);
144+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_CATALOG, RANGER_CATALOG);
145+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_SCHEMA, RANGER_SCHEMA);
146+
}
147+
148+
private void assertTableResource(RangerServiceResource resource) {
149+
assertResourceElementCount(resource, 3);
150+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_CATALOG, RANGER_CATALOG);
151+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_SCHEMA, RANGER_SCHEMA);
152+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_TABLE, RANGER_TABLE);
153+
}
154+
155+
private void assertColumnResource(RangerServiceResource resource) {
156+
assertResourceElementCount(resource, 4);
157+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_CATALOG, RANGER_CATALOG);
158+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_SCHEMA, RANGER_SCHEMA);
159+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_TABLE, RANGER_TABLE);
160+
assertResourceElementValue(resource, RANGER_TYPE_TRINO_COLUMN, RANGER_COLUMN);
161+
}
162+
163+
private void assertResourceElementValue(RangerServiceResource resource, String resourceName, String value) {
164+
Assertions.assertTrue(resource.getResourceElements().containsKey(resourceName));
165+
Assertions.assertNotNull(resource.getResourceElements().get(resourceName).getValues());
166+
Assertions.assertEquals(1, resource.getResourceElements().get(resourceName).getValues().size());
167+
Assertions.assertEquals(value, resource.getResourceElements().get(resourceName).getValues().get(0));
168+
}
169+
170+
private void assertException(RangerAtlasEntity entity, String exceptionMessage) {
171+
try {
172+
RangerServiceResource resource = resourceMapper.buildResource(entity);
173+
174+
Assertions.fail("Expected buildResource() to fail. But it returned " + resource);
175+
} catch (Exception excp) {
176+
Assertions.assertTrue(excp.getMessage().startsWith(exceptionMessage), "Unexpected exception message: expected=" + exceptionMessage + "; found " + excp.getMessage());
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)