Skip to content

Commit 2853d39

Browse files
Skip scanning objects that were removed on platform side since the last scan time, so that integration tests are less flaky (#922)
## Changes Adds handling of objects that may not be present and the API is throwing an exception to indicate this. While it is not expected to happen, during testing this is triggered at times and causes failures of the integration tests. Log instead and continue. ### Linked issues Resolves #897
1 parent baf3984 commit 2853d39

File tree

12 files changed

+184
-12
lines changed

12 files changed

+184
-12
lines changed

src/databricks/labs/ucx/assessment/init_scripts.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from dataclasses import dataclass
66

77
from databricks.sdk import WorkspaceClient
8+
from databricks.sdk.errors import ResourceDoesNotExist
89

910
from databricks.labs.ucx.assessment.crawlers import (
1011
_AZURE_SP_CONF_FAILURE_MSG,
@@ -62,7 +63,11 @@ def _assess_global_init_scripts(self, all_global_init_scripts):
6263
failures="[]",
6364
)
6465
failures = []
65-
script = self._ws.global_init_scripts.get(gis.script_id)
66+
try:
67+
script = self._ws.global_init_scripts.get(gis.script_id)
68+
except ResourceDoesNotExist:
69+
logger.warning(f"removed on the backend {gis.script_id}")
70+
continue
6671
global_init_script = base64.b64decode(script.script).decode("utf-8")
6772
if not global_init_script:
6873
continue

src/databricks/labs/ucx/hive_metastore/table_migrate.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,11 @@ def _alias_table(
379379
return False
380380

381381
def _reapply_grants(self, from_table_name, to_table_name, *, target_view: bool = False):
382-
grants = self._ws.grants.get(SecurableType.TABLE, from_table_name)
382+
try:
383+
grants = self._ws.grants.get(SecurableType.TABLE, from_table_name)
384+
except NotFound:
385+
logger.warning(f"removed on the backend {from_table_name}")
386+
return
383387
if grants.privilege_assignments is not None:
384388
logger.info(f"Applying grants on table {to_table_name}")
385389
grants_changes = []

src/databricks/labs/ucx/install.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,11 @@ def _cluster_node_type(self, spec: compute.ClusterSpec) -> compute.ClusterSpec:
782782
return replace(spec, gcp_attributes=compute.GcpAttributes(availability=compute.GcpAvailability.ON_DEMAND_GCP))
783783

784784
def _check_policy_has_instance_pool(self, policy_id):
785-
policy = self._ws.cluster_policies.get(policy_id=policy_id)
785+
try:
786+
policy = self._ws.cluster_policies.get(policy_id=policy_id)
787+
except NotFound:
788+
logger.warning(f"removed on the backend {policy_id}")
789+
return False
786790
def_json = json.loads(policy.definition)
787791
instance_pool = def_json.get("instance_pool_id")
788792
if instance_pool is not None:

src/databricks/labs/ucx/installer/hms_lineage.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from databricks.labs.blueprint.tui import Prompts
66
from databricks.sdk import WorkspaceClient
7+
from databricks.sdk.errors import InvalidParameterValue
78
from databricks.sdk.service.compute import GlobalInitScriptDetailsWithContent
89

910
global_init_script = """if [[ $DB_IS_DRIVER = "TRUE" ]]; then
@@ -48,7 +49,11 @@ def _check_lineage_spark_config_exists(self) -> GlobalInitScriptDetailsWithConte
4849
for script in self._ws.global_init_scripts.list():
4950
if not script.script_id:
5051
continue
51-
script_content = self._ws.global_init_scripts.get(script_id=script.script_id)
52+
try:
53+
script_content = self._ws.global_init_scripts.get(script_id=script.script_id)
54+
except InvalidParameterValue as err:
55+
logger.warning(f"Failed to get init script {script.script_id}: {err}")
56+
continue
5257
if not script_content:
5358
continue
5459
content = script_content.script

src/databricks/labs/ucx/workspace_access/listing.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from itertools import groupby
66

77
from databricks.sdk import WorkspaceClient
8-
from databricks.sdk.errors import InternalError, NotFound
8+
from databricks.sdk.errors import InternalError, NotFound, ResourceDoesNotExist
99
from databricks.sdk.retries import retried
1010
from databricks.sdk.service.workspace import ObjectInfo, ObjectType
1111

@@ -76,7 +76,11 @@ def is_dir(x: ObjectInfo) -> bool:
7676
def walk(self, start_path="/"):
7777
self.start_time = dt.datetime.now()
7878
logger.info(f"Recursive WorkspaceFS listing started at {self.start_time}")
79-
root_object = self._ws.workspace.get_status(start_path)
79+
try:
80+
root_object = self._ws.workspace.get_status(start_path)
81+
except ResourceDoesNotExist:
82+
logger.warning(f"removed on the backend {start_path}")
83+
return self.results
8084
self.results.append(root_object)
8185

8286
with ThreadPoolExecutor(self._num_threads) as executor:

src/databricks/labs/ucx/workspace_access/verification.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import Literal
33

44
from databricks.sdk import WorkspaceClient
5-
from databricks.sdk.errors import PermissionDenied
5+
from databricks.sdk.errors import NotFound, PermissionDenied, ResourceDoesNotExist
66

77
from databricks.labs.ucx.workspace_access.groups import MigrationState
88
from databricks.labs.ucx.workspace_access.secrets import SecretScopesSupport
@@ -33,7 +33,11 @@ def verify_applied_permissions(
3333
target: Literal["backup", "account"],
3434
):
3535
base_attr = "temporary_name" if target == "backup" else "name_in_account"
36-
op = self._ws.permissions.get(object_type, object_id)
36+
try:
37+
op = self._ws.permissions.get(object_type, object_id)
38+
except ResourceDoesNotExist:
39+
logger.warning(f"removed on backend: {object_type}.{object_id}")
40+
return
3741
for info in migration_state.groups:
3842
if not op.access_control_list:
3943
continue
@@ -78,8 +82,16 @@ def verify_roles_and_entitlements(self, migration_state: MigrationState, target:
7882
comparison_base = getattr(el, "id_in_workspace" if target == "backup" else "id_in_workspace")
7983
comparison_target = getattr(el, target_attr)
8084

81-
base_group_info = self._ws.groups.get(comparison_base)
82-
target_group_info = self._ws.groups.get(comparison_target)
85+
try:
86+
base_group_info = self._ws.groups.get(comparison_base)
87+
except NotFound:
88+
logger.warning(f"removed on backend: {comparison_base}")
89+
continue
90+
try:
91+
target_group_info = self._ws.groups.get(comparison_target)
92+
except NotFound:
93+
logger.warning(f"removed on backend: {comparison_target}")
94+
continue
8395

8496
assert base_group_info.roles == target_group_info.roles
8597
assert base_group_info.entitlements == target_group_info.entitlements

tests/unit/assessment/test_init_scripts.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import base64
22

3+
from databricks.sdk.errors import ResourceDoesNotExist
34
from databricks.sdk.service.compute import GlobalInitScriptDetails
45

56
from databricks.labs.ucx.assessment.init_scripts import (
@@ -102,3 +103,27 @@ def test_init_script_without_config_should_have_empty_creator_name(mocker):
102103
script_id="222", script_name="newscript", enabled=False, created_by=None, success=1, failures="[]"
103104
),
104105
]
106+
107+
108+
def test_missing_global_init_script(mocker, caplog):
109+
mock_ws = mocker.Mock()
110+
mock_ws.global_init_scripts.list.return_value = [
111+
GlobalInitScriptDetails(
112+
created_at=111,
113+
created_by=None,
114+
enabled=False,
115+
name="newscript",
116+
position=4,
117+
script_id="222",
118+
updated_at=111,
119+
updated_by="[email protected]",
120+
)
121+
]
122+
mock_ws.global_init_scripts.get.side_effect = ResourceDoesNotExist("RESOURCE_DOES_NOT_EXIST")
123+
mockbackend = MockBackend()
124+
crawler = GlobalInitScriptCrawler(mock_ws, mockbackend, schema="ucx")
125+
result = crawler.snapshot()
126+
result = mockbackend.rows_written_for("hive_metastore.ucx.global_init_scripts", "append")
127+
128+
assert len(result) == 0
129+
assert "removed on the backend 222" in caplog.messages

tests/unit/hive_metastore/test_table_move.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,36 @@ def test_alias_tables_not_found_view_unknown_error(caplog):
204204
assert len([rec.message for rec in caplog.records if "unknown error" in rec.message]) == 1
205205

206206

207+
def test_move_tables_get_grants_fails_because_table_removed(caplog):
208+
client = create_autospec(WorkspaceClient)
209+
210+
client.tables.list.return_value = [
211+
TableInfo(
212+
catalog_name="SrcC",
213+
schema_name="SrcS",
214+
name="table1",
215+
full_name="SrcC.SrcS.table1",
216+
table_type=TableType.EXTERNAL,
217+
),
218+
]
219+
220+
rows = {
221+
"SHOW CREATE TABLE SrcC.SrcS.table1": [
222+
["CREATE TABLE SrcC.SrcS.table1 (name string)"],
223+
]
224+
}
225+
226+
client.grants.get.side_effect = NotFound('TABLE_DOES_NOT_EXIST')
227+
client.schemas.get.side_effect = [SchemaInfo(), SchemaInfo()]
228+
client.tables.get.side_effect = [NotFound(), NotFound(), NotFound(), NotFound()]
229+
client.grants.update = MagicMock()
230+
backend = MockBackend(rows=rows)
231+
tm = TableMove(client, backend)
232+
tm.move_tables("SrcC", "SrcS", "table1", "TgtC", "TgtS", False)
233+
234+
assert "removed on the backend SrcC.SrcS.table1" in caplog.messages
235+
236+
207237
def test_move_all_tables_and_drop_source():
208238
client = create_autospec(WorkspaceClient)
209239

tests/unit/installer/test_hms_lineage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from databricks.labs.blueprint.tui import MockPrompts
44
from databricks.sdk import WorkspaceClient
5+
from databricks.sdk.errors import InvalidParameterValue
56
from databricks.sdk.service.compute import (
67
GlobalInitScriptDetails,
78
GlobalInitScriptDetailsWithContent,
@@ -127,3 +128,26 @@ def test_disabled_and_then_enabled(caplog):
127128
hmle.apply(MockPrompts({r'HMS lineage collection init script is disabled.*': 'yes'}))
128129

129130
ws.global_init_scripts.update.assert_called_once()
131+
132+
133+
def test_get_script_fails_missing_script(caplog):
134+
ws = create_autospec(WorkspaceClient)
135+
ginit_scripts = [
136+
GlobalInitScriptDetails(
137+
created_at=1695045723722,
138+
created_by="[email protected]",
139+
enabled=True,
140+
name="test123",
141+
position=0,
142+
script_id="12345",
143+
updated_at=1695046359612,
144+
updated_by="[email protected]",
145+
)
146+
]
147+
ws.global_init_scripts.list.return_value = ginit_scripts
148+
ws.global_init_scripts.get.side_effect = InvalidParameterValue("INVALID_PARAMETER_VALUE")
149+
150+
with caplog.at_level('WARN'):
151+
hmle = HiveMetastoreLineageEnabler(ws)
152+
hmle.apply(MockPrompts({r'No HMS lineage collection init script exists.*': 'yes'}))
153+
assert "Failed to get init script 12345: INVALID_PARAMETER_VALUE" in caplog.messages

tests/unit/test_install.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,19 @@ def test_repair_run_result_state(ws, caplog, mock_installation_with_jobs, any_pr
834834

835835
workspace_installation.repair_run("assessment")
836836
assert "Please try after sometime" in caplog.text
837+
838+
839+
def test_check_policy_has_instance_pool(ws, mock_installation, any_prompt, caplog):
840+
ws.cluster_policies.get = MagicMock()
841+
ws.cluster_policies.get.side_effect = NotFound("NO_POLICY")
842+
843+
sql_backend = MockBackend()
844+
wheels = create_autospec(WheelsV2)
845+
config = WorkspaceConfig(inventory_database='ucx')
846+
timeout = timedelta(seconds=1)
847+
workspace_installation = WorkspaceInstallation(
848+
config, mock_installation_with_jobs, sql_backend, wheels, ws, any_prompt, timeout
849+
)
850+
res = workspace_installation._check_policy_has_instance_pool(1)
851+
assert not res
852+
assert "removed on the backend 1" in caplog.messages

0 commit comments

Comments
 (0)