Skip to content

Commit 9f27f85

Browse files
yzhu0ahsan-z-khan
authored andcommitted
fix: Collapse cross-account artifacts in query lineage response (aws#2796)
1 parent a9a0ea2 commit 9f27f85

File tree

2 files changed

+178
-1
lines changed

2 files changed

+178
-1
lines changed

src/sagemaker/lineage/query.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,44 @@ def _convert_api_response(self, response) -> LineageQueryResult:
212212

213213
return converted
214214

215+
def _collapse_cross_account_artifacts(self, query_response):
216+
"""Collapse the duplicate vertices and edges for cross-account."""
217+
for edge in query_response.edges:
218+
if (
219+
"artifact" in edge.source_arn
220+
and "artifact" in edge.destination_arn
221+
and edge.source_arn.split("/")[1] == edge.destination_arn.split("/")[1]
222+
and edge.source_arn != edge.destination_arn
223+
):
224+
edge_source_arn = edge.source_arn
225+
edge_destination_arn = edge.destination_arn
226+
self._update_cross_account_edge(
227+
edges=query_response.edges,
228+
arn=edge_source_arn,
229+
duplicate_arn=edge_destination_arn,
230+
)
231+
self._update_cross_account_vertex(
232+
query_response=query_response, duplicate_arn=edge_destination_arn
233+
)
234+
235+
# remove the duplicate edges from cross account
236+
new_edge = [e for e in query_response.edges if not e.source_arn == e.destination_arn]
237+
query_response.edges = new_edge
238+
239+
return query_response
240+
241+
def _update_cross_account_edge(self, edges, arn, duplicate_arn):
242+
"""Replace the duplicate arn with arn in edges list."""
243+
for idx, e in enumerate(edges):
244+
if e.destination_arn == duplicate_arn:
245+
edges[idx].destination_arn = arn
246+
elif e.source_arn == duplicate_arn:
247+
edges[idx].source_arn = arn
248+
249+
def _update_cross_account_vertex(self, query_response, duplicate_arn):
250+
"""Remove the vertex with duplicate arn in the vertices list."""
251+
query_response.vertices = [v for v in query_response.vertices if not v.arn == duplicate_arn]
252+
215253
def query(
216254
self,
217255
start_arns: List[str],
@@ -239,5 +277,7 @@ def query(
239277
Filters=query_filter._to_request_dict() if query_filter else {},
240278
MaxDepth=max_depth,
241279
)
280+
query_response = self._convert_api_response(query_response)
281+
query_response = self._collapse_cross_account_artifacts(query_response)
242282

243-
return self._convert_api_response(query_response)
283+
return query_response

tests/unit/sagemaker/lineage/test_query.py

+137
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,143 @@ def test_lineage_query(sagemaker_session):
4545
assert response.vertices[1].lineage_entity == "Context"
4646

4747

48+
def test_lineage_query_cross_account_same_artifact(sagemaker_session):
49+
lineage_query = LineageQuery(sagemaker_session)
50+
sagemaker_session.sagemaker_client.query_lineage.return_value = {
51+
"Vertices": [
52+
{
53+
"Arn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
54+
"Type": "Endpoint",
55+
"LineageType": "Artifact",
56+
},
57+
{
58+
"Arn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
59+
"Type": "Endpoint",
60+
"LineageType": "Artifact",
61+
},
62+
],
63+
"Edges": [
64+
{
65+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
66+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
67+
"AssociationType": "SAME_AS",
68+
},
69+
{
70+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
71+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
72+
"AssociationType": "SAME_AS",
73+
},
74+
],
75+
}
76+
77+
response = lineage_query.query(
78+
start_arns=["arn:aws:sagemaker:us-west-2:0123456789012:context/mycontext"]
79+
)
80+
assert len(response.edges) == 0
81+
assert len(response.vertices) == 1
82+
assert (
83+
response.vertices[0].arn
84+
== "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0"
85+
)
86+
assert response.vertices[0].lineage_source == "Endpoint"
87+
assert response.vertices[0].lineage_entity == "Artifact"
88+
89+
90+
def test_lineage_query_cross_account(sagemaker_session):
91+
lineage_query = LineageQuery(sagemaker_session)
92+
sagemaker_session.sagemaker_client.query_lineage.return_value = {
93+
"Vertices": [
94+
{
95+
"Arn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
96+
"Type": "Endpoint",
97+
"LineageType": "Artifact",
98+
},
99+
{
100+
"Arn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
101+
"Type": "Endpoint",
102+
"LineageType": "Artifact",
103+
},
104+
{
105+
"Arn": "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd",
106+
"Type": "Endpoint",
107+
"LineageType": "Artifact",
108+
},
109+
{
110+
"Arn": "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9efgh",
111+
"Type": "Endpoint",
112+
"LineageType": "Artifact",
113+
},
114+
],
115+
"Edges": [
116+
{
117+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
118+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
119+
"AssociationType": "SAME_AS",
120+
},
121+
{
122+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
123+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0",
124+
"AssociationType": "SAME_AS",
125+
},
126+
{
127+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678902:artifact/e1f29799189751939405b0f2b5b9d2a0",
128+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd",
129+
"AssociationType": "ABC",
130+
},
131+
{
132+
"SourceArn": "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd",
133+
"DestinationArn": "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9efgh",
134+
"AssociationType": "DEF",
135+
},
136+
],
137+
}
138+
139+
response = lineage_query.query(
140+
start_arns=["arn:aws:sagemaker:us-west-2:0123456789012:context/mycontext"]
141+
)
142+
143+
assert len(response.edges) == 2
144+
assert (
145+
response.edges[0].source_arn
146+
== "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0"
147+
)
148+
assert (
149+
response.edges[0].destination_arn
150+
== "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd"
151+
)
152+
assert response.edges[0].association_type == "ABC"
153+
154+
assert (
155+
response.edges[1].source_arn
156+
== "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd"
157+
)
158+
assert (
159+
response.edges[1].destination_arn
160+
== "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9efgh"
161+
)
162+
assert response.edges[1].association_type == "DEF"
163+
164+
assert len(response.vertices) == 3
165+
assert (
166+
response.vertices[0].arn
167+
== "arn:aws:sagemaker:us-east-2:012345678901:artifact/e1f29799189751939405b0f2b5b9d2a0"
168+
)
169+
assert response.vertices[0].lineage_source == "Endpoint"
170+
assert response.vertices[0].lineage_entity == "Artifact"
171+
assert (
172+
response.vertices[1].arn
173+
== "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9abcd"
174+
)
175+
assert response.vertices[1].lineage_source == "Endpoint"
176+
assert response.vertices[1].lineage_entity == "Artifact"
177+
assert (
178+
response.vertices[2].arn
179+
== "arn:aws:sagemaker:us-east-2:012345678903:artifact/e1f29799189751939405b0f2b5b9efgh"
180+
)
181+
assert response.vertices[2].lineage_source == "Endpoint"
182+
assert response.vertices[2].lineage_entity == "Artifact"
183+
184+
48185
def test_vertex_to_object_endpoint_context(sagemaker_session):
49186
vertex = Vertex(
50187
arn="arn:aws:sagemaker:us-west-2:0123456789012:context/mycontext",

0 commit comments

Comments
 (0)