Skip to content

Commit 3b9918f

Browse files
authored
Fix migrate-locations issues (#4379)
Fixed issues with `migrate-locations`. Replaced . (dot) with _ (underscore) in generated external location name. Skipped external-locations that generate "BadRequest" exception due to an existing external location. closes #4378
1 parent bca711d commit 3b9918f

File tree

2 files changed

+28
-9
lines changed

2 files changed

+28
-9
lines changed

src/databricks/labs/ucx/aws/locations.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from databricks.labs.ucx.hive_metastore.locations import ExternalLocation
1010

1111
from databricks.sdk import WorkspaceClient
12+
from databricks.sdk.errors.platform import BadRequest
1213

1314
logger = logging.getLogger(__name__)
1415

@@ -52,18 +53,22 @@ def run(self) -> None:
5253
logger.error(f"Missing credential for role {role_arn} for path {path}")
5354
continue
5455
location_name = self._generate_external_location_name(path)
55-
self._ws.external_locations.create(
56-
location_name,
57-
path,
58-
credential_dict[role_arn],
59-
skip_validation=True,
60-
fallback=self._enable_hms_federation,
61-
)
56+
try:
57+
self._ws.external_locations.create(
58+
location_name,
59+
path,
60+
credential_dict[role_arn],
61+
skip_validation=True,
62+
fallback=self._enable_hms_federation,
63+
)
64+
except BadRequest as e:
65+
logger.warning(f"Failed to create external location for path {path} with error: {e}")
66+
continue
6267
self._principal_acl.apply_location_acl()
6368

6469
@staticmethod
6570
def _generate_external_location_name(path: str) -> str:
66-
return "_".join(Path(path.lower()).parts[1:])
71+
return "_".join(Path(path.lower().replace(".", "_")).parts[1:])
6772

6873
@staticmethod
6974
def _identify_missing_external_locations(

tests/unit/aws/test_access.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from databricks.labs.ucx.hive_metastore import ExternalLocations, TablesCrawler
3939
from databricks.labs.ucx.hive_metastore.grants import PrincipalACL
4040
from databricks.labs.ucx.hive_metastore.locations import ExternalLocation, MountsCrawler
41+
from databricks.sdk.errors.platform import BadRequest
4142
from tests.unit import DEFAULT_CONFIG
4243

4344

@@ -142,6 +143,7 @@ def backend():
142143
["s3://BUCKET2/FOLDER2", 1],
143144
["s3://BUCKET4", 2],
144145
["s3://BUCKETX/FOLDERX", 1],
146+
["s3://BUCKETX/FOLDER.COM", 1],
145147
]
146148
}
147149
return MockBackend(rows=rows, fails_on_first={})
@@ -183,6 +185,7 @@ def test_create_external_locations(mock_ws, installation_multiple_roles, backend
183185
call('bucket1_folder1', 's3://BUCKET1/FOLDER1', 'cred1', skip_validation=True, fallback=False),
184186
call('bucket2_folder2', 's3://BUCKET2/FOLDER2', 'cred1', skip_validation=True, fallback=False),
185187
call('bucketx_folderx', 's3://BUCKETX/FOLDERX', 'credx', skip_validation=True, fallback=False),
188+
call('bucketx_folder_com', 's3://BUCKETX/FOLDER.COM', 'credx', skip_validation=True, fallback=False),
186189
]
187190
mock_ws.external_locations.create.assert_has_calls(calls, any_order=True)
188191
aws.get_role_policy.assert_not_called()
@@ -224,6 +227,10 @@ def test_create_external_locations_skip_existing(mock_ws, backend, locations):
224227
mock_ws.external_locations.list.return_value = [
225228
ExternalLocationInfo(name="UCX_FOO_1", url="s3://BUCKETX/FOLDERX", credential_name="credx"),
226229
]
230+
231+
mock_ws.external_locations.create.side_effect = [None, BadRequest("Location already exists"), None, None]
232+
# Check that the existing location is skipped without breaking the migration
233+
227234
aws = create_autospec(AWSResources)
228235
principal_acl = create_autospec(PrincipalACL)
229236
config = install.load(WorkspaceConfig)
@@ -235,6 +242,7 @@ def test_create_external_locations_skip_existing(mock_ws, backend, locations):
235242
principal_acl,
236243
)
237244
external_locations_migration.run()
245+
238246
calls = [
239247
call("bucket1_folder1", 's3://BUCKET1/FOLDER1', 'cred1', skip_validation=True, fallback=False),
240248
]
@@ -293,7 +301,13 @@ def test_create_uber_principal_existing_role_in_policy(mock_ws, mock_installatio
293301
'UCX_MIGRATION_ROLE_ucx',
294302
'UCX_MIGRATION_POLICY_ucx',
295303
AWSResourceType.S3,
296-
{'s3://BUCKET1/FOLDER1', 's3://BUCKET2/FOLDER2', 's3://BUCKET4', 's3://BUCKETX/FOLDERX'},
304+
{
305+
's3://BUCKET1/FOLDER1',
306+
's3://BUCKET2/FOLDER2',
307+
's3://BUCKET4',
308+
's3://BUCKETX/FOLDERX',
309+
's3://BUCKETX/FOLDER.COM',
310+
},
297311
None,
298312
None,
299313
)

0 commit comments

Comments
 (0)