Skip to content

Commit a1f31e6

Browse files
authored
Run integration test with a single group (#152)
To remove nightly test flakiness, this PR replaces `_create_groups` utilities with `make_ucx_group`, which relies on the common fixture factories.
1 parent f15848b commit a1f31e6

File tree

6 files changed

+83
-255
lines changed

6 files changed

+83
-255
lines changed

src/databricks/labs/ucx/providers/groups_info.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ def __init__(self):
1919
def add(self, group: MigrationGroupInfo):
2020
self.groups.append(group)
2121

22+
def is_in_scope(self, attr: str, group: Group) -> bool:
23+
for info in self.groups:
24+
if getattr(info, attr).id == group.id:
25+
return True
26+
return False
27+
2228
def get_by_workspace_group_name(self, workspace_group_name: str) -> MigrationGroupInfo | None:
2329
found = [g for g in self.groups if g.workspace.display_name == workspace_group_name]
2430
if len(found) == 0:

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import BinaryIO
99

1010
import pytest
11-
from databricks.sdk import WorkspaceClient
11+
from databricks.sdk import AccountClient, WorkspaceClient
1212
from databricks.sdk.core import DatabricksError
1313
from databricks.sdk.service import compute, iam, jobs, pipelines, workspace
1414

@@ -31,10 +31,8 @@ def inner(**kwargs):
3131
_LOG.debug(f"removing {name} fixture: {x}")
3232
remove(x)
3333
except DatabricksError as e:
34-
if e.error_code in ("RESOURCE_DOES_NOT_EXIST",):
35-
_LOG.debug(f"ignoring error while {name} {x} teardown: {e}")
36-
continue
37-
raise e
34+
# TODO: fix on the databricks-labs-pytester level
35+
_LOG.debug(f"ignoring error while {name} {x} teardown: {e}")
3836

3937

4038
@pytest.fixture
@@ -55,6 +53,13 @@ def ws() -> WorkspaceClient:
5553
return WorkspaceClient()
5654

5755

56+
@pytest.fixture(scope="session")
57+
def acc() -> AccountClient:
58+
# Use variables from Unified Auth
59+
# See https://databricks-sdk-py.readthedocs.io/en/latest/authentication.html
60+
return AccountClient()
61+
62+
5863
@pytest.fixture
5964
def make_secret_scope(ws, make_random):
6065
def create(**kwargs):
@@ -125,19 +130,35 @@ def _scim_values(ids: list[str]) -> list[iam.ComplexValue]:
125130
return [iam.ComplexValue(value=x) for x in ids]
126131

127132

128-
@pytest.fixture
129-
def make_group(ws, make_random):
133+
def _make_group(name, interface, make_random):
130134
def create(
131-
*, members: list[str] | None = None, roles: list[str] | None = None, display_name: str | None = None, **kwargs
135+
*,
136+
members: list[str] | None = None,
137+
roles: list[str] | None = None,
138+
entitlements: list[str] | None = None,
139+
display_name: str | None = None,
140+
**kwargs,
132141
):
133142
kwargs["display_name"] = f"sdk-{make_random(4)}" if display_name is None else display_name
134143
if members is not None:
135144
kwargs["members"] = _scim_values(members)
136145
if roles is not None:
137146
kwargs["roles"] = _scim_values(roles)
138-
return ws.groups.create(**kwargs)
147+
if entitlements is not None:
148+
kwargs["entitlements"] = _scim_values(entitlements)
149+
return interface.create(**kwargs)
150+
151+
yield from factory(name, create, lambda item: interface.delete(item.id))
152+
153+
154+
@pytest.fixture
155+
def make_group(ws, make_random):
156+
yield from _make_group("workspace group", ws.groups, make_random)
139157

140-
yield from factory("workspace group", create, lambda item: ws.groups.delete(item.id))
158+
159+
@pytest.fixture
160+
def make_acc_group(acc, make_random):
161+
yield from _make_group("account group", acc.groups, make_random)
141162

142163

143164
@pytest.fixture

tests/integration/conftest.py

Lines changed: 33 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
import logging
44
import os
55
import random
6-
import uuid
76
from functools import partial
87

98
import databricks.sdk.core
109
import pytest
11-
from _pytest.fixtures import SubRequest
1210
from databricks.sdk import AccountClient, WorkspaceClient
1311
from databricks.sdk.core import Config, DatabricksError
1412
from databricks.sdk.service.compute import (
@@ -46,8 +44,6 @@
4644
EnvironmentInfo,
4745
InstanceProfile,
4846
WorkspaceObjects,
49-
_cleanup_groups,
50-
_create_groups,
5147
_get_basic_job_cluster,
5248
_get_basic_task,
5349
_set_random_permissions,
@@ -220,48 +216,30 @@ def test_table_fixture(make_table):
220216
logger.info(f'Created new view in new schema: {make_table(view=True, ctas="SELECT 2+2 AS four")}')
221217

222218

223-
@pytest.fixture(scope="session")
224-
def env(ws: WorkspaceClient, acc: AccountClient, request: SubRequest) -> EnvironmentInfo:
225-
# prepare environment
226-
test_uid = f"{UCX_TESTING_PREFIX}_{str(uuid.uuid4())[:8]}"
227-
logger.debug(f"Creating environment with uid {test_uid}")
228-
groups = _create_groups(ws, acc, test_uid, NUM_TEST_GROUPS, Threader)
229-
230-
def post_cleanup():
231-
print("\n")
232-
logger.debug("Cleaning up the environment")
233-
logger.debug("Deleting test groups")
234-
cleanups = [partial(_cleanup_groups, ws, acc, g) for g in groups]
235-
236-
def error_silencer(func):
237-
def _wrapped(*args, **kwargs):
238-
try:
239-
func(*args, **kwargs)
240-
except Exception as e:
241-
logger.warning(f"Cannot delete temp group, skipping it. Original exception {e}")
242-
243-
return _wrapped
244-
245-
silent_delete = error_silencer(ws.groups.delete)
246-
247-
temp_cleanups = [
248-
# TODO: this is too heavy for SCIM API, refactor to ID lookup
249-
partial(silent_delete, g.id)
250-
for g in ws.groups.list(filter=f"displayName sw 'db-temp-{test_uid}'")
251-
]
252-
new_ws_groups_cleanups = [
253-
partial(silent_delete, g.id) for g in ws.groups.list(filter=f"displayName sw '{test_uid}'")
254-
]
219+
@pytest.fixture
220+
def user_pool(ws):
221+
return list(ws.users.list(filter="displayName sw 'test-user-'", attributes="id, userName, displayName"))
255222

256-
all_cleanups = cleanups + temp_cleanups + new_ws_groups_cleanups
257-
Threader(all_cleanups).run()
258-
logger.debug(f"Finished cleanup for the environment {test_uid}")
259223

260-
request.addfinalizer(post_cleanup)
261-
yield EnvironmentInfo(test_uid=test_uid, groups=groups)
224+
@pytest.fixture
225+
def make_ucx_group(make_random, make_group, make_acc_group, user_pool):
226+
def inner():
227+
display_name = f"ucx_{make_random(4)}"
228+
members = [_.id for _ in random.choices(user_pool, k=random.randint(1, 40))]
229+
ws_group = make_group(display_name=display_name, members=members, entitlements=["allow-cluster-create"])
230+
acc_group = make_acc_group(display_name=display_name, members=members)
231+
return ws_group, acc_group
262232

233+
return inner
263234

264-
@pytest.fixture(scope="session")
235+
236+
@pytest.fixture
237+
def env(make_ucx_group, make_random) -> EnvironmentInfo:
238+
test_uid = f"ucx_{make_random(4)}"
239+
yield EnvironmentInfo(test_uid=test_uid, groups=[make_ucx_group()])
240+
241+
242+
@pytest.fixture
265243
def instance_profiles(env: EnvironmentInfo, ws: WorkspaceClient) -> list[InstanceProfile]:
266244
logger.debug("Adding test instance profiles")
267245
profiles: list[InstanceProfile] = []
@@ -295,7 +273,7 @@ def instance_profiles(env: EnvironmentInfo, ws: WorkspaceClient) -> list[Instanc
295273
logger.debug("Test instance profiles deleted")
296274

297275

298-
@pytest.fixture(scope="session")
276+
@pytest.fixture
299277
def instance_pools(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreateInstancePoolResponse]:
300278
logger.debug("Creating test instance pools")
301279

@@ -320,7 +298,7 @@ def instance_pools(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreateInst
320298
Threader(executables).run()
321299

322300

323-
@pytest.fixture(scope="session")
301+
@pytest.fixture
324302
def pipelines(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreatePipelineResponse]:
325303
logger.debug("Creating test DLT pipelines")
326304

@@ -350,7 +328,7 @@ def pipelines(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreatePipelineR
350328
Threader(executables).run()
351329

352330

353-
@pytest.fixture(scope="session")
331+
@pytest.fixture
354332
def jobs(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreateResponse]:
355333
logger.debug("Creating test jobs")
356334

@@ -377,7 +355,7 @@ def jobs(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreateResponse]:
377355
Threader(executables).run()
378356

379357

380-
@pytest.fixture(scope="session")
358+
@pytest.fixture
381359
def cluster_policies(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreatePolicyResponse]:
382360
logger.debug("Creating test cluster policies")
383361

@@ -412,7 +390,7 @@ def cluster_policies(env: EnvironmentInfo, ws: WorkspaceClient) -> list[CreatePo
412390
Threader(executables).run()
413391

414392

415-
@pytest.fixture(scope="session")
393+
@pytest.fixture
416394
def clusters(env: EnvironmentInfo, ws: WorkspaceClient) -> list[ClusterDetails]:
417395
logger.debug("Creating test clusters")
418396

@@ -447,7 +425,7 @@ def clusters(env: EnvironmentInfo, ws: WorkspaceClient) -> list[ClusterDetails]:
447425
logger.debug("Test clusters deleted")
448426

449427

450-
@pytest.fixture(scope="session")
428+
@pytest.fixture
451429
def experiments(ws: WorkspaceClient, env: EnvironmentInfo) -> list[CreateExperimentResponse]:
452430
logger.debug("Creating test experiments")
453431

@@ -480,7 +458,7 @@ def experiments(ws: WorkspaceClient, env: EnvironmentInfo) -> list[CreateExperim
480458
logger.debug("Test experiments deleted")
481459

482460

483-
@pytest.fixture(scope="session")
461+
@pytest.fixture
484462
def models(ws: WorkspaceClient, env: EnvironmentInfo) -> list[ModelDatabricks]:
485463
logger.debug("Creating models")
486464

@@ -513,7 +491,7 @@ def models(ws: WorkspaceClient, env: EnvironmentInfo) -> list[ModelDatabricks]:
513491
logger.debug("Test models deleted")
514492

515493

516-
@pytest.fixture(scope="session")
494+
@pytest.fixture
517495
def warehouses(ws: WorkspaceClient, env: EnvironmentInfo) -> list[GetWarehouseResponse]:
518496
logger.debug("Creating warehouses")
519497

@@ -548,13 +526,13 @@ def warehouses(ws: WorkspaceClient, env: EnvironmentInfo) -> list[GetWarehouseRe
548526
logger.debug("Test warehouses deleted")
549527

550528

551-
@pytest.fixture(scope="session")
529+
@pytest.fixture
552530
def tokens(ws: WorkspaceClient, env: EnvironmentInfo) -> list[AccessControlRequest]:
553531
logger.debug("Adding token-level permissions to groups")
554532

555533
token_permissions = [
556534
AccessControlRequest(group_name=ws_group.display_name, permission_level=PermissionLevel.CAN_USE)
557-
for ws_group, _ in random.sample(env.groups, k=NUM_TEST_TOKENS)
535+
for ws_group, _ in random.sample(env.groups, k=min(len(env.groups), NUM_TEST_TOKENS))
558536
]
559537

560538
ws.permissions.update(
@@ -566,7 +544,7 @@ def tokens(ws: WorkspaceClient, env: EnvironmentInfo) -> list[AccessControlReque
566544
yield token_permissions
567545

568546

569-
@pytest.fixture(scope="session")
547+
@pytest.fixture
570548
def secret_scopes(ws: WorkspaceClient, env: EnvironmentInfo) -> list[SecretScope]:
571549
logger.debug("Creating test secret scopes")
572550

@@ -587,7 +565,7 @@ def secret_scopes(ws: WorkspaceClient, env: EnvironmentInfo) -> list[SecretScope
587565
Threader(executables).run()
588566

589567

590-
@pytest.fixture(scope="session")
568+
@pytest.fixture
591569
def workspace_objects(ws: WorkspaceClient, env: EnvironmentInfo) -> WorkspaceObjects:
592570
logger.info(f"Creating test workspace objects under /{env.test_uid}")
593571
ws.workspace.mkdirs(f"/{env.test_uid}")
@@ -642,7 +620,7 @@ def workspace_objects(ws: WorkspaceClient, env: EnvironmentInfo) -> WorkspaceObj
642620
logger.debug("Test workspace objects deleted")
643621

644622

645-
@pytest.fixture(scope="session")
623+
@pytest.fixture
646624
def verifiable_objects(
647625
clusters,
648626
instance_pools,

tests/integration/test_e2e.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,8 @@ def test_e2e(
181181
toolkit = GroupMigrationToolkit(config)
182182
toolkit.prepare_environment()
183183

184-
logger.debug("Verifying that the groups were created")
185-
186-
assert len(ws.groups.list(filter=f"displayName sw '{config.groups.backup_group_prefix}{env.test_uid}'")) == len(
187-
toolkit.group_manager.migration_groups_provider.groups
188-
)
189-
190-
assert len(ws.groups.list(filter=f"displayName sw '{env.test_uid}'")) == len(
191-
toolkit.group_manager.migration_groups_provider.groups
192-
)
193-
194-
assert len(toolkit.group_manager._list_account_level_groups(filter=f"displayName sw '{env.test_uid}'")) == len(
195-
toolkit.group_manager.migration_groups_provider.groups
196-
)
197-
198-
for _info in toolkit.group_manager.migration_groups_provider.groups:
184+
group_migration_state = toolkit.group_manager.migration_groups_provider
185+
for _info in group_migration_state.groups:
199186
_ws = ws.groups.get(id=_info.workspace.id)
200187
_backup = ws.groups.get(id=_info.backup.id)
201188
_ws_members = sorted([m.value for m in _ws.members])
@@ -216,28 +203,28 @@ def test_e2e(
216203
for _objects, id_attribute, request_object_type in verifiable_objects:
217204
_verify_group_permissions(_objects, id_attribute, request_object_type, ws, toolkit, "backup")
218205

219-
_verify_roles_and_entitlements(toolkit.group_manager.migration_groups_provider, ws, "backup")
206+
_verify_roles_and_entitlements(group_migration_state, ws, "backup")
220207

221208
toolkit.replace_workspace_groups_with_account_groups()
222209

223-
new_groups = list(ws.groups.list(filter=f"displayName sw '{env.test_uid}'", attributes="displayName,meta"))
224-
assert len(new_groups) == len(toolkit.group_manager.migration_groups_provider.groups)
210+
new_groups = [
211+
_ for _ in ws.groups.list(attributes="displayName,meta") if group_migration_state.is_in_scope("account", _)
212+
]
213+
assert len(new_groups) == len(group_migration_state.groups)
225214
assert all(g.meta.resource_type == "Group" for g in new_groups)
226215

227216
toolkit.apply_permissions_to_account_groups()
228217

229218
for _objects, id_attribute, request_object_type in verifiable_objects:
230219
_verify_group_permissions(_objects, id_attribute, request_object_type, ws, toolkit, "account")
231220

232-
_verify_roles_and_entitlements(toolkit.group_manager.migration_groups_provider, ws, "account")
221+
_verify_roles_and_entitlements(group_migration_state, ws, "account")
233222

234223
toolkit.delete_backup_groups()
235224

236-
backup_groups = list(
237-
ws.groups.list(
238-
filter=f"displayName sw '{config.groups.backup_group_prefix}{env.test_uid}'", attributes="displayName,meta"
239-
)
240-
)
225+
backup_groups = [
226+
_ for _ in ws.groups.list(attributes="displayName,meta") if group_migration_state.is_in_scope("backup", _)
227+
]
241228
assert len(backup_groups) == 0
242229

243230
toolkit.cleanup_inventory_table()

0 commit comments

Comments
 (0)