Skip to content

Commit 1a4fe3e

Browse files
authored
Integration tests: purge suffixes and RemoveAfter tags for fixtures that don't already have this (#2388)
## Changes This PR updates all remaining integration test fixtures so that where possible any resources they create have a `RemoveAfter` tag or the equivalent suffix on their name. (This prevents the resource from being removed from the test environment while the test is underway.) Doing this in a single PR is intended to reduce future effort tracking down spurious test failures when this occurs. The main remaining resource that is often deleted while integration tests are underway is the folder for the UCX installation itself. Unfortunately the name of this folder is fixed and tightly coupled to the generated product-name being used for the test. Dealing with this is non-trivial and has been omitted from this PR. ### Linked issues Supersedes #2369. ### Tests - integration tests
1 parent ca79618 commit 1a4fe3e

File tree

1 file changed

+31
-17
lines changed

1 file changed

+31
-17
lines changed

src/databricks/labs/ucx/mixins/fixtures.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@
3333
TableType,
3434
)
3535
from databricks.sdk.service.dashboards import Dashboard as SDKDashboard
36+
from databricks.sdk.service.ml import ModelTag
3637
from databricks.sdk.service.serving import (
3738
EndpointCoreConfigInput,
3839
ServedModelInput,
3940
ServedModelInputWorkloadSize,
4041
ServingEndpointDetailed,
42+
EndpointTag,
4143
)
4244
from databricks.sdk.service.sql import (
4345
CreateWarehouseRequestWarehouseType,
@@ -120,7 +122,7 @@ def fresh_wheel_file(tmp_path) -> Path:
120122
@pytest.fixture
121123
def wsfs_wheel(ws, fresh_wheel_file, make_random):
122124
my_user = ws.current_user.me().user_name
123-
workspace_location = f"/Users/{my_user}/wheels/{make_random(10)}"
125+
workspace_location = f"/Users/{my_user}/wheels/{make_random(10)}-{get_purge_suffix()}"
124126
ws.workspace.mkdirs(workspace_location)
125127

126128
wsfs_wheel = f"{workspace_location}/{fresh_wheel_file.name}"
@@ -582,7 +584,7 @@ def create(
582584
overwrite: bool = False,
583585
) -> str:
584586
if path is None:
585-
path = f"/Users/{ws.current_user.me().user_name}/sdk-{make_random(4)}"
587+
path = f"/Users/{ws.current_user.me().user_name}/sdk-{make_random(4)}-{get_purge_suffix()}"
586588
elif isinstance(path, pathlib.Path):
587589
path = str(path)
588590
if content is None:
@@ -609,7 +611,7 @@ def create(*, path: str | None = None):
609611
def make_repo(ws, make_random):
610612
def create(*, url=None, provider=None, path=None, **kwargs):
611613
if path is None:
612-
path = f"/Repos/{ws.current_user.me().user_name}/sdk-{make_random(4)}"
614+
path = f"/Repos/{ws.current_user.me().user_name}/sdk-{make_random(4)}-{get_purge_suffix()}"
613615
if url is None:
614616
url = "https://github.com/shreyas-goenka/empty-repo.git"
615617
if provider is None:
@@ -760,9 +762,10 @@ def create(
760762
**kwargs,
761763
):
762764
if path is None:
763-
path = f"/Users/{ws.current_user.me().user_name}/{make_random(4)}"
765+
path = f"/Users/{ws.current_user.me().user_name}/{make_random(4)}-{get_purge_suffix()}"
764766
if experiment_name is None:
765-
experiment_name = f"sdk-{make_random(4)}"
767+
# The purge suffix is needed here as well, just in case the path was supplied.
768+
experiment_name = f"sdk-{make_random(4)}-{get_purge_suffix()}"
766769

767770
try:
768771
ws.workspace.mkdirs(path)
@@ -842,6 +845,11 @@ def create(
842845
):
843846
if model_name is None:
844847
model_name = f"sdk-{make_random(4)}"
848+
remove_after_tag = ModelTag(key="RemoveAfter", value=get_test_purge_time())
849+
if 'tags' not in kwargs:
850+
kwargs["tags"] = [remove_after_tag]
851+
else:
852+
kwargs["tags"].append(remove_after_tag)
845853

846854
created_model = ws.model_registry.create_model(model_name, **kwargs)
847855
model = ws.model_registry.get_model(created_model.registered_model.name)
@@ -854,7 +862,7 @@ def create(
854862
def make_pipeline(ws, make_random, make_notebook):
855863
def create(**kwargs) -> pipelines.CreatePipelineResponse:
856864
if "name" not in kwargs:
857-
kwargs["name"] = f"sdk-{make_random(4)}"
865+
kwargs["name"] = f"sdk-{make_random(4)}-{get_purge_suffix()}"
858866
if "libraries" not in kwargs:
859867
kwargs["libraries"] = [pipelines.PipelineLibrary(notebook=pipelines.NotebookLibrary(path=make_notebook()))]
860868
if "clusters" not in kwargs:
@@ -863,9 +871,7 @@ def create(**kwargs) -> pipelines.CreatePipelineResponse:
863871
node_type_id=ws.clusters.select_node_type(local_disk=True, min_memory_gb=16),
864872
label="default",
865873
num_workers=1,
866-
custom_tags={
867-
"cluster_type": "default",
868-
},
874+
custom_tags={"cluster_type": "default", "RemoveAfter": get_test_purge_time()},
869875
)
870876
]
871877
return ws.pipelines.create(continuous=False, **kwargs)
@@ -965,6 +971,8 @@ def inventory_schema(make_schema):
965971
@pytest.fixture
966972
def make_catalog(ws, sql_backend, make_random) -> Generator[Callable[..., CatalogInfo], None, None]:
967973
def create() -> CatalogInfo:
974+
# Warning: As of 2024-09-04 there is no way to mark this catalog for protection against the watchdog.
975+
# Ref: https://github.com/databrickslabs/watchdog/blob/cdc97afdac1567e89d3b39d938f066fd6267b3ba/scan/objects/uc.go#L68
968976
name = f"ucx_C{make_random(4)}".lower()
969977
sql_backend.execute(f"CREATE CATALOG {name}")
970978
catalog_info = ws.catalogs.get(name)
@@ -1042,6 +1050,7 @@ def create( # pylint: disable=too-many-locals,too-many-arguments,too-many-state
10421050
elif non_delta:
10431051
table_type = TableType.EXTERNAL # pylint: disable=redefined-variable-type
10441052
data_source_format = DataSourceFormat.JSON
1053+
# DBFS locations are not purged; no suffix necessary.
10451054
storage_location = f"dbfs:/tmp/ucx_test_{make_random(4)}"
10461055
# Modified, otherwise it will identify the table as a DB Dataset
10471056
ddl = (
@@ -1151,7 +1160,10 @@ def create(
11511160
schema_name = schema.name
11521161

11531162
if name is None:
1154-
name = f"ucx_T{make_random(4)}".lower()
1163+
name = f"ucx_t{make_random(4).lower()}"
1164+
1165+
# Note: the Watchdog does not explicitly scan for functions; they are purged along with their parent schema.
1166+
# As such the function can't be marked (and doesn't need to be if the schema as marked) for purge protection.
11551167

11561168
full_name = f"{catalog_name}.{schema_name}.{name}".lower()
11571169
if hive_udf:
@@ -1173,7 +1185,7 @@ def create(
11731185
full_name=full_name,
11741186
)
11751187

1176-
logger.info(f"Function {udf_info.full_name} crated")
1188+
logger.info(f"Function {udf_info.full_name} created")
11771189
return udf_info
11781190

11791191
def remove(udf_info: FunctionInfo):
@@ -1192,7 +1204,7 @@ def remove(udf_info: FunctionInfo):
11921204
def make_query(ws, make_table, make_random) -> Generator[LegacyQuery, None, None]:
11931205
def create() -> LegacyQuery:
11941206
table = make_table()
1195-
query_name = f"ucx_query_Q{make_random(4)}"
1207+
query_name = f"ucx_query_Q{make_random(4)}_{get_purge_suffix()}"
11961208
query = ws.queries_legacy.create(
11971209
name=query_name,
11981210
description="TEST QUERY FOR UCX",
@@ -1256,6 +1268,7 @@ def create() -> Wait[ServingEndpointDetailed]:
12561268
)
12571269
]
12581270
),
1271+
tags=[EndpointTag(key="RemoveAfter", value=get_test_purge_time())],
12591272
)
12601273
return endpoint
12611274

@@ -1328,7 +1341,8 @@ def make_mounted_location(make_random, make_dbfs_data_copy, env_or_skip):
13281341
location; the mounted location is made with fixture setup already.
13291342
"""
13301343
existing_mounted_location = f'dbfs:/mnt/{env_or_skip("TEST_MOUNT_NAME")}/a/b/c'
1331-
new_mounted_location = f'dbfs:/mnt/{env_or_skip("TEST_MOUNT_NAME")}/a/b/{make_random(4)}'
1344+
# DBFS locations are not purged; no suffix necessary.
1345+
new_mounted_location = f'dbfs:/mnt/{env_or_skip("TEST_MOUNT_NAME")}/a/b/{make_random(4)}-{get_purge_suffix()}'
13321346
make_dbfs_data_copy(src_path=existing_mounted_location, dst_path=new_mounted_location)
13331347
return new_mounted_location
13341348

@@ -1377,7 +1391,7 @@ def create() -> Dashboard:
13771391
},
13781392
)
13791393

1380-
dashboard_name = f"ucx_D{make_random(4)}"
1394+
dashboard_name = f"ucx_D{make_random(4)}_{get_purge_suffix()}"
13811395
dashboard = ws.dashboards.create(name=dashboard_name, tags=["original_dashboard_tag"])
13821396
assert dashboard.id is not None
13831397
ws.dashboard_widgets.create(
@@ -1445,10 +1459,10 @@ def make_lakeview_dashboard(ws, make_random, env_or_skip):
14451459
}
14461460

14471461
def create(display_name: str = "") -> SDKDashboard:
1448-
if len(display_name) == 0:
1449-
display_name = f"created_by_ucx_{make_random()}"
1450-
else:
1462+
if display_name:
14511463
display_name = f"{display_name} ({make_random()})"
1464+
else:
1465+
display_name = f"created_by_ucx_{make_random()}_{get_purge_suffix()}"
14521466
dashboard = ws.lakeview.create(
14531467
display_name,
14541468
serialized_dashboard=json.dumps(serialized_dashboard),

0 commit comments

Comments
 (0)