Skip to content

Commit 5f1baa1

Browse files
Merge pull request #212 from dattawalake/Issue_211
added dlt sink demo and silver spec table name ui component
2 parents 387abbb + 157fbf4 commit 5f1baa1

File tree

8 files changed

+116
-16
lines changed

8 files changed

+116
-16
lines changed

docs/content/faq/app_faq.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
title: "App"
3+
date: 2025-08-29T14:50:11-04:00
4+
weight: 63
5+
draft: false
6+
---
7+
8+
**Q.1. Do I need to run an initial setup before using the DLT Meta App ?**
9+
Yes. Before you can use the DLT Meta App, you must click the Setup button to create the required DLT meta environment. This initializes the app and enables you to onboard or manage Delta Live Tables (DLT) pipelines.
10+
11+
**Q. 2. What are the main features of the DLT Meta App ?**
12+
The DLT Meta App provides several key capabilities:
13+
14+
Onboard new DLT pipelines through an interactive interface.
15+
Deploy and manage DLT pipelines directly in the app.
16+
Run DLT meta app demo flows to explore example pipelines and usage patterns.
17+
Use the command-line interface (CLI) to automate onboarding, deployment, and management operations.
18+
19+
**Q. 3. Who can access and use the DLT Meta App ?**
20+
Only authenticated Databricks workspace users with appropriate permissions can access and use the app:
21+
22+
You need CAN_USE permission to run the app and CAN_MANAGE permission to administer it.
23+
The app can be shared within your workspace or account, but not with external users.
24+
Every user must log in with their Databricks account credentials.
25+
26+
**Q. 4. How does catalog and schema access work in the DLT Meta App ?**
27+
By default, the app uses a dedicated Service Principal (SP) identity for all data and resource access:
28+
29+
The SP must have explicit permissions (such as USE CATALOG, USE SCHEMA, SELECT) on all Unity Catalog resources the DLT pipelines reference.
30+
If a user accesses the app—even via the same URL—their abilities depend on the SP’s granted access. If the SP lacks permissions, the app’s functionality fails for all users.
31+
Optionally, the app can be configured to use users’ own permissions via On-Behalf-Of (OBO) mode, but this requires additional setup.
32+
33+
**Q. 5. How should I resolve access errors or permission issues in the app ?**
34+
If you experience errors related to catalog, schema, or table access:
35+
36+
Verify the app’s Service Principal has the required permissions in Unity Catalog.
37+
Confirm the app is attached to the necessary resources, such as warehouses or secrets.
38+
Check if recent administrative or sharing changes have affected your privileges.
39+
Review audit logs for permission denials or configuration changes.
40+
Consult your Databricks workspace administrator if necessary.
41+
42+
**Q. 6. How is sharing, security, and isolation managed for the DLT Meta App ?**
43+
The app operates in a multi-tenant platform, but provides strong isolation between customer accounts and apps.
44+
Each app runs on a dedicated, isolated, serverless compute environment.
45+
Sharing is restricted to specific users, groups, or all account users; every sharing and permission event is audit-logged.
46+
There is no option for public or anonymous access—only Databricks account users can run the app.
47+
48+
**Q. 7. What are best practices for securing and operating the DLT Meta App ?**
49+
Grant only the minimum required catalog and schema permissions to the app’s Service Principal (principle of least privilege).
50+
Regularly review all permission and sharing changes using audit logs.
51+
Allow only trusted application code to run, especially if enabling OBO mode.
52+
Use workspace monitoring tools and collaborate with your Databricks administrator for access adjustments or troubleshooting.

docs/static/images/app_cli.png

81.5 KB
Loading
75.4 KB
Loading
160 KB
Loading
52.6 KB
Loading

lakehouse_app/app.py

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import logging
88
import errno
99
import re
10-
# Use pty to create a pseudo-terminal for better interactive support
1110
import pty
1211
import select
1312
import fcntl
@@ -16,7 +15,6 @@
1615
import signal
1716
import json
1817

19-
# Configure logging
2018
logging.basicConfig(level=logging.INFO,
2119
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
2220
handlers=[logging.FileHandler("dlt-meta-app.log"),
@@ -227,15 +225,15 @@ def start_command():
227225
if 'PYTHONPATH' not in os.environ or not os.path.isdir(os.environ.get('PYTHONPATH', '')):
228226
commands = [
229227
"pip install databricks-cli",
230-
# "git clone https://github.com/databrickslabs/dlt-meta.git",
231-
"git clone https://github.com/dattawalake/dlt-meta.git",
228+
"git clone https://github.com/databrickslabs/dlt-meta.git",
232229
f"python -m venv {current_directory}/dlt-meta/.venv",
233230
f"export HOME={current_directory}",
234231
"cd dlt-meta",
235232
"source .venv/bin/activate",
236233
f"export PYTHONPATH={current_directory}/dlt-meta/",
237234
"pwd",
238235
"pip install databricks-sdk",
236+
"pip install PyYAML",
239237
]
240238
print("Start setting up dlt-meta environment")
241239
for c in commands:
@@ -322,6 +320,7 @@ def handle_onboard_form():
322320
"silver_schema": request.form.get('silver_schema', 'dltmeta_silver_7b4e981029b843c799bf61a0a121b3ca'),
323321
"dlt_meta_layer": request.form.get('dlt_meta_layer', '1'),
324322
"bronze_table": request.form.get('bronze_table', 'bronze_dataflowspec'),
323+
"silver_table": request.form.get('silver_table', 'silver_dataflowspec'),
325324
"overwrite": "1" if request.form.get('overwrite') == "1" else "0",
326325
"version": request.form.get('version', 'v1'),
327326
"environment": request.form.get('environment', 'prod'),
@@ -375,26 +374,67 @@ def handle_deploy_form():
375374
def run_demo():
376375
code_to_run = request.json.get('demo_name', '')
377376
print(f"processing demo for :{request.json}")
378-
current_directory = os.environ['PYTHONPATH'] # os.getcwd()
377+
current_directory = os.environ['PYTHONPATH']
379378
demo_dict = {"demo_cloudfiles": "demo/launch_af_cloudfiles_demo.py",
380379
"demo_acf": "demo/launch_acfs_demo.py",
381380
"demo_silverfanout": "demo/launch_silver_fanout_demo.py",
382-
"demo_dias": "demo/launch_dais_demo.py"
381+
"demo_dias": "demo/launch_dais_demo.py",
382+
"demo_dlt_sink": "demo/launch_dlt_sink_demo.py",
383+
"demo_dabs": "demo/generate_dabs_resources.py"
383384
}
384385
demo_file = demo_dict.get(code_to_run, None)
385386
uc_name = request.json.get('uc_name', '')
386-
result = subprocess.run(f"python {current_directory}/{demo_file} --uc_catalog_name {uc_name} --profile DEFAULT",
387-
shell=True,
388-
capture_output=True,
389-
text=True
390-
)
387+
388+
if code_to_run == 'demo_dabs':
389+
390+
# Step 1: Generate Databricks resources
391+
subprocess.run(f"python {current_directory}/{demo_file} --uc_catalog_name {uc_name} "
392+
f"--source=cloudfiles --profile DEFAULT",
393+
shell=True,
394+
capture_output=True,
395+
text=True
396+
)
397+
398+
# Step 2: Change working directory to demo/dabs for all next commands
399+
subprocess.run("databricks bundle validate --profile=DEFAULT", cwd=f"{current_directory}/demo/dabs",
400+
shell=True,
401+
capture_output=True,
402+
text=True)
403+
404+
# Step 4: Deploy the bundle
405+
subprocess.run("databricks bundle deploy --target dev --profile=DEFAULT",
406+
cwd=f"{current_directory}/demo/dabs", shell=True,
407+
capture_output=True,
408+
text=True)
409+
410+
# Step 5: Run 'onboard_people' task
411+
rs1 = subprocess.run("databricks bundle run onboard_people -t dev --profile=DEFAULT",
412+
cwd=f"{current_directory}/demo/dabs", shell=True,
413+
capture_output=True,
414+
text=True)
415+
print(f"onboarding completed: {rs1.stdout}")
416+
# Step 6: Run 'execute_pipelines_people' task
417+
result = subprocess.run("databricks bundle run execute_pipelines_people -t dev --profile=DEFAULT",
418+
cwd=f"{current_directory}/demo/dabs",
419+
shell=True,
420+
capture_output=True,
421+
text=True
422+
)
423+
print(f"execution of pipeline completed: {result.stdout}")
424+
else:
425+
result = subprocess.run(f"python {current_directory}/{demo_file} --uc_catalog_name {uc_name} "
426+
f"--profile DEFAULT",
427+
shell=True,
428+
capture_output=True,
429+
text=True
430+
)
391431
return extract_command_output(result)
392432

393433

394434
def extract_command_output(result):
395435
stdout = result.stdout
396436
job_id_match = re.search(r"job_id=(\d+) | pipeline=(\d+)", stdout)
397-
url_match = re.search(r"url=(https?://[^\s]+)", stdout)
437+
url_match = re.search(r"(https?://[^\s]+)", stdout)
398438

399439
job_id = job_id_match.group(1) or job_id_match.group(2) if job_id_match else None
400440
job_url = url_match.group(1) if url_match else None

lakehouse_app/templates/landingPage.html

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,11 @@ <h2 class='step-heading'>Step 1 : Onboarding</h2>
655655
<input id="bronze_table" name="bronze_table" placeholder="Enter bronze table name"
656656
type="text" value="bronze_dataflowspec">
657657
</div>
658+
<div class="form-group">
659+
<label>Provide silver dataflow spec table name:</label>
660+
<input id="silver_table" name="silver_table" placeholder="Enter silver table name"
661+
type="text" value="silver_dataflowspec">
662+
</div>
658663
<div class="form-group">
659664
<label>Overwrite dataflow spec?</label>
660665
<div class="radio-group">
@@ -841,6 +846,8 @@ <h3 class='step-heading'>Available Demos</h3>
841846
<button class="command-button2" data-command="demo_acf">Demo Apply Changes Snapshot</button>
842847
<button class="command-button2" data-command="demo_silverfanout">Demo Silver fanout</button>
843848
<button class="command-button2" data-command="demo_dias">Demo Dias</button>
849+
<button class="command-button2" data-command="demo_dlt_sink">Demo Sink</button>
850+
844851

845852
</div>
846853

@@ -956,7 +963,7 @@ <h5 class="modal-title">Please wait...</h5>
956963
const modalContent = `
957964
<div class="modal-content">
958965
<h3 class='step-heading'>${data.modal_content.title}</h3>
959-
<p>Job ID: ${data.modal_content.job_id}</p>
966+
${data.modal_content.job_id ? `<p>Job ID: ${data.modal_content.job_id}</p>`:""}
960967
961968
<p><a href="${url}" target="_blank">Open Job in Databricks</a></p >
962969
@@ -994,7 +1001,8 @@ <h3 class='step-heading'>${data.modal_content.title}</h3>
9941001
const modalContent = `
9951002
<div class="modal-content">
9961003
<h3 class='step-heading'>${data.modal_content.title}</h3>
997-
<p>Job ID: ${data.modal_content.job_id}</p>
1004+
${data.modal_content.job_id ? `<p>Job ID: ${data.modal_content.job_id}</p>
1005+
`:""}
9981006
9991007
<p><a href="${url}" target="_blank">Open Job in Databricks</a></p >
10001008
@@ -1067,7 +1075,7 @@ <h3 class='step-heading'>${data.modal_content.title}</h3>
10671075
const modalContent = `
10681076
<div class="modal-content">
10691077
<h3 class='step-heading'>${data.modal_content.title}</h3>
1070-
<p>Job ID: ${data.modal_content.job_id}</p>
1078+
${data.modal_content.job_id ? `<p>Job ID: ${data.modal_content.job_id}</p>`:""}
10711079
10721080
<p><a href="${url}" target="_blank">Open Job in Databricks</a></p >
10731081

src/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ def _load_onboard_config_ui(self, form_data) -> OnboardCommand:
638638
onboard_cmd_dict["bronze_dataflowspec_path"] = f'{self._install_folder()}/bronze_dataflow_specs'
639639

640640
if onboard_cmd_dict["onboard_layer"] == "silver" or onboard_cmd_dict["onboard_layer"] == "bronze_silver":
641-
onboard_cmd_dict["silver_dataflowspec_table"] = 'silver_dataflowspec' # Not in form, using default
641+
onboard_cmd_dict["silver_dataflowspec_table"] = form_data.get('silver_table', 'silver_dataflowspec')
642642
if not onboard_cmd_dict["uc_enabled"]:
643643
onboard_cmd_dict["silver_dataflowspec_path"] = f'{self._install_folder()}/silver_dataflow_specs'
644644

0 commit comments

Comments
 (0)