Skip to content

Commit 8aed930

Browse files
authored
Fixed legacy table ACL ownership migration and other integration testing issues (#722)
Close #717
1 parent 8b2c72d commit 8aed930

File tree

22 files changed

+363
-416
lines changed

22 files changed

+363
-416
lines changed

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ ignore = [
132132
# Ignore complexity
133133
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
134134
# Ignore flaky Import block is un-sorted or un-formatted
135-
"I001"
135+
"I001",
136+
# Ignore Exception must not use a string literal, assign to variable first
137+
"EM101",
136138
]
137139
extend-exclude = [
138140
"notebooks/*.py"

src/databricks/labs/ucx/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ def validate_external_locations():
9696
installation = installation_manager.for_user(ws.current_user.me())
9797
sql_backend = StatementExecutionBackend(ws, installation.config.warehouse_id)
9898
location_crawler = ExternalLocations(ws, sql_backend, installation.config.inventory_database)
99-
path = location_crawler.save_as_terraform_definitions_on_workspace()
100-
if len(path) > 0 and prompts.confirm(f"external_locations.tf file written to {path}. Do you want to open it?"):
99+
path = location_crawler.save_as_terraform_definitions_on_workspace(installation.path)
100+
if path and prompts.confirm(f"external_locations.tf file written to {path}. Do you want to open it?"):
101101
webbrowser.open(f"{ws.config.host}/#workspace{path}")
102102

103103

src/databricks/labs/ucx/framework/parallel.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, errs):
2626
class Threads(Generic[Result]):
2727
def __init__(self, name, tasks: Sequence[Task[Result]], num_threads: int):
2828
self._name = name
29-
self._tasks = tasks
29+
self._tasks = list(tasks)
3030
self._task_fail_error_pct = 50
3131
self._num_threads = num_threads
3232
self._started = dt.datetime.now()
@@ -48,6 +48,12 @@ def gather(
4848
num_threads = MIN_THREADS
4949
return cls(name, tasks, num_threads=num_threads)._run()
5050

51+
@classmethod
52+
def strict(cls, name: str, tasks: Sequence[Task[Result]]):
53+
_, errs = cls.gather(name, tasks)
54+
if errs:
55+
raise ManyError(errs)
56+
5157
def _run(self) -> tuple[Iterable[Result], list[Exception]]:
5258
given_cnt = len(self._tasks)
5359
if given_cnt == 0:

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,17 @@ def this_type_and_key(self):
7373
anonymous_function=self.anonymous_function,
7474
)
7575

76-
def hive_grant_sql(self) -> str:
76+
def hive_grant_sql(self) -> list[str]:
7777
object_type, object_key = self.this_type_and_key()
7878
# See https://docs.databricks.com/en/sql/language-manual/security-grant.html
79-
if self.action_type.upper() == "OWN":
80-
return f"ALTER {object_type} {object_key} OWNER TO `{self.principal}`"
81-
else:
82-
return f"GRANT {self.action_type} ON {object_type} {object_key} TO `{self.principal}`"
79+
statements = []
80+
actions = self.action_type.split(", ")
81+
if "OWN" in actions:
82+
actions.remove("OWN")
83+
statements.append(f"ALTER {object_type} {object_key} OWNER TO `{self.principal}`")
84+
if actions:
85+
statements.append(f"GRANT {', '.join(actions)} ON {object_type} {object_key} TO `{self.principal}`")
86+
return statements
8387

8488
def hive_revoke_sql(self) -> str:
8589
object_type, object_key = self.this_type_and_key()

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ class ExternalLocations(CrawlerBase[ExternalLocation]):
3333
def __init__(self, ws: WorkspaceClient, sbe: SqlBackend, schema):
3434
super().__init__(sbe, "hive_metastore", schema, "external_locations", ExternalLocation)
3535
self._ws = ws
36-
self._folder = f"/Users/{ws.current_user.me().user_name}/.ucx"
3736

3837
def _external_locations(self, tables: list[Row], mounts) -> Iterable[ExternalLocation]:
3938
min_slash = 2
@@ -173,9 +172,7 @@ def _match_table_external_locations(self) -> tuple[list[list], list[ExternalLoca
173172
missing_locations.append(loc)
174173
return matching_locations, missing_locations
175174

176-
def save_as_terraform_definitions_on_workspace(self, folder: str | None = None) -> str:
177-
if folder:
178-
self._folder = folder
175+
def save_as_terraform_definitions_on_workspace(self, folder: str) -> str | None:
179176
matching_locations, missing_locations = self._match_table_external_locations()
180177
if len(matching_locations) > 0:
181178
logger.info("following external locations are already configured.")
@@ -190,13 +187,12 @@ def save_as_terraform_definitions_on_workspace(self, folder: str | None = None)
190187
for script in self._get_ext_location_definitions(missing_locations):
191188
buffer.write(script)
192189
buffer.seek(0)
193-
return self._overwrite_mapping(buffer)
194-
else:
195-
logger.info("no additional external location to be created.")
196-
return ""
190+
return self._overwrite_mapping(folder, buffer)
191+
logger.info("no additional external location to be created.")
192+
return None
197193

198-
def _overwrite_mapping(self, buffer) -> str:
199-
path = f"{self._folder}/external_locations.tf"
194+
def _overwrite_mapping(self, folder, buffer) -> str:
195+
path = f"{folder}/external_locations.tf"
200196
self._ws.workspace.upload(path, buffer, overwrite=True, format=ImportFormat.AUTO)
201197
return path
202198

0 commit comments

Comments
 (0)