Skip to content

Add PyDABs support for SQL Alerts - #6335

Merged
Sankalp-Mittal merged 8 commits into
mainfrom
sankalp-mittal/pydabs-support-for-alerts
Aug 27, 2026
Merged

Add PyDABs support for SQL Alerts#6335
Sankalp-Mittal merged 8 commits into
mainfrom
sankalp-mittal/pydabs-support-for-alerts

Conversation

@Sankalp-Mittal

@Sankalp-Mittal Sankalp-Mittal commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds support for using alerts via Python

def load_resources() -> Resources:
    resources = Resources()

    resources.add_alert(
        "my_alert_2",
        {
            "display_name": "My Alert (2)",
            "query_text": "SELECT 2",
            "warehouse_id": "my_warehouse_2",
            "evaluation": {
                "comparison_operator": "LESS_THAN",
                "source": {"name": "column_2"},
            },
            "schedule": {
                "quartz_cron_schedule": "0 0 12 * * ?",
                "timezone_id": "UTC",
            },
        },
    )

    return resources

Changes

  • Add PyDABs (Python) support for the alert resource: register resources.Alert for codegen and load the sql and iam namespace (packages.py), wire Alert into the core package (add_alert, alerts collection, alert_mutator, resource-type registration), and commit the generated databricks/bundles/alerts/ package.

Why

This change was requested by a PyDABs user, and is part of eventually supporting all resources in PyDABs

Tests

  • Unit: added an Alert case to test_resources.py (add/merge/mutator/duplicate/location).
  • Acceptance: new alerts-support scenario validating YAML + Python alerts coexist, serialize (nested evaluation/schedule + enum), and are mutated by alert_mutator, on both terraform and direct engines.

@Sankalp-Mittal Sankalp-Mittal changed the title sankalp mittal/pydabs support for alerts Add PyDABs support for SQL Alerts Aug 20, 2026
@@ -0,0 +1,19 @@
from enum import Enum

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire directory _models/ is autogenerated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they auto-generated one-off or every time in CI?

Let's mark generated files as such:

  1. header comment
  2. change .gitattributes
  3. Taskfile dependency if not already

@@ -0,0 +1,123 @@
__all__ = [

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is also auto generated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, mark the whole python/databricks/bundles/alerts/* as auto-gen'ed

@@ -12,6 +12,7 @@
"VariableOrDict",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These had to be manually added to add mutators

@@ -69,6 +70,38 @@ def my_job_mutator(bundle: Bundle, job: Job) -> Job:
# was deemed overly implicit and potentially confusing.


Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again manually added but standard template code

@@ -31,6 +31,7 @@ def all(cls) -> tuple["_ResourceType", ...]:
# intentionally lazily load all resource types to avoid imports from databricks.bundles.core to
# be imported in databricks.bundles.<resource_type>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should also be autogenerated we should just have a cli interface where singular and plural can be defined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicate from resources in go - we should only have one source of truth

@@ -249,6 +258,34 @@ def add_volume(

self._volumes[resource_name] = volume

def add_alert(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only function that I'm not sure on whether to autogenerate or not??

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks boilerplate compared to the other resources

@@ -74,6 +83,40 @@ class TestCase:
),
resource_types[Schema],
),
(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the unit test params that I think need to be manually written

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to procedurally generate these test cases, or at least use the ones from invariant tests as starting point and transform them to the python tests

@@ -26,6 +34,7 @@ class TestCase:
dict_example: dict
dataclass_example: Resource
mutator: Callable
article: str = "a" # grammatical article in the duplicate-resource error message

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default article can be overriden for example in the case of alert

@@ -21,6 +22,8 @@
"pipelines",
"resources",
"catalog",
"sql",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 had to be added manually but I think they can be found out using BFS and potentially stored in a yaml file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, as follow-up: Consider inverting LOADED_NAMESPACES to always load all except the ~2 problematic ones

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 0824197

Run: 33060077897

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 1 4 274 1191 3:55
💚​ aws windows 1 4 276 1189 3:45
💚​ azure linux 1 4 273 1191 3:53
💚​ azure windows 1 4 275 1189 4:00
💚​ gcp linux 1 4 274 1191 4:24
💚​ gcp windows 1 4 276 1189 3:45
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 3 slowest tests (at least 2 minutes):
duration env testname
3:56 azure windows TestAccept
3:41 gcp windows TestAccept
3:40 aws windows TestAccept

@Sankalp-Mittal
Sankalp-Mittal marked this pull request as ready for review August 21, 2026 12:43

@janniklasrose janniklasrose left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to merge. consider all comments for follow-up work

@@ -0,0 +1,19 @@
from enum import Enum

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they auto-generated one-off or every time in CI?

Let's mark generated files as such:

  1. header comment
  2. change .gitattributes
  3. Taskfile dependency if not already

@@ -21,6 +22,8 @@
"pipelines",
"resources",
"catalog",
"sql",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, as follow-up: Consider inverting LOADED_NAMESPACES to always load all except the ~2 problematic ones

@@ -0,0 +1,123 @@
__all__ = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, mark the whole python/databricks/bundles/alerts/* as auto-gen'ed

@@ -31,6 +31,7 @@ def all(cls) -> tuple["_ResourceType", ...]:
# intentionally lazily load all resource types to avoid imports from databricks.bundles.core to
# be imported in databricks.bundles.<resource_type>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicate from resources in go - we should only have one source of truth

@@ -249,6 +258,34 @@ def add_volume(

self._volumes[resource_name] = volume

def add_alert(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks boilerplate compared to the other resources


if self._alerts.get(resource_name):
self.add_diagnostic_error(
msg=f"Duplicate resource name '{resource_name}' for an alert. Resource names must be unique.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid the test refactoring below, I'd suggest using different wording for all errors to not have to worry about this (or add article to _ResourceType)

Suggested change
msg=f"Duplicate resource name '{resource_name}' for an alert. Resource names must be unique.",
msg=f"Duplicate resource name '{resource_name}' for resource 'alert'. Resource names must be unique.",

@@ -74,6 +83,40 @@ class TestCase:
),
resource_types[Schema],
),
(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to procedurally generate these test cases, or at least use the ones from invariant tests as starting point and transform them to the python tests

Comment on lines +6 to +10
python:
resources:
- "resources:load_resources"
mutators:
- "mutators:update_alert"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now ok, but let's make sure when we do full auto-gen that we test the conflict keys etc from test_resources.py in acceptance tests for more confidence

@Sankalp-Mittal
Sankalp-Mittal force-pushed the sankalp-mittal/pydabs-support-for-alerts branch from 43bc6f1 to 0824197 Compare August 27, 2026 09:45
@Sankalp-Mittal
Sankalp-Mittal added this pull request to the merge queue Aug 27, 2026
Merged via the queue into main with commit 5766a14 Aug 27, 2026
29 checks passed
@Sankalp-Mittal
Sankalp-Mittal deleted the sankalp-mittal/pydabs-support-for-alerts branch August 27, 2026 10:23
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 5766a14

Run: 33062892871

Env ❌​FAIL 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🔄​ aws linux 3 2 2 1224 972 103:12
🔄​ aws windows 3 2 2 1161 992 141:02
💚​ azure linux 3 2 1117 1010 88:57
💚​ azure windows 3 2 1054 1030 112:45
❌​ gcp linux 4 1 2 2 1103 1014 100:59
❌​ gcp windows 4 1 2 2 1040 1034 127:58
13 interesting tests: 4 flaky, 4 FAIL, 2 RECOVERED, 2 SKIP, 1 KNOWN
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
🟨​ TestAccept 🔄​f 🔄​f 💚​R 💚​R 🟨​K 🟨​K
💚​ TestAccept/bundle/invariant/no_drift 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🔄​ TestAccept/bundle/resources/apps/lifecycle-started ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/catalogs/comment_out_of_band ✅​p ✅​p ✅​p ✅​p ❌​F ❌​F
❌​ TestAccept/bundle/resources/catalogs/comment_out_of_band/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p ❌​F ❌​F
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate 🔄​f ✅​p 🙈​s 🙈​s 🙈​s 🙈​s
🔄​ TestAccept/bundle/resources/postgres_synced_tables/recreate/DATABRICKS_BUNDLE_ENGINE=terraform 🔄​f ✅​p
❌​ TestAccept/bundle/resources/schemas/comment_out_of_band ✅​p ✅​p ✅​p ✅​p ❌​F ❌​F
❌​ TestAccept/bundle/resources/schemas/comment_out_of_band/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p ❌​F ❌​F
💚​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 50 slowest tests (at least 2 minutes):
duration env testname
25:15 aws windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
25:11 aws windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
15:28 aws linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
14:54 aws windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
14:47 aws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
13:33 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
13:26 gcp windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
13:18 gcp linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
11:38 azure linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:03 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:17 aws linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
8:10 aws windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:58 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
7:46 aws windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:41 azure windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:40 azure linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:33 gcp windows TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
7:31 azure windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:27 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
7:19 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
7:16 gcp windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
7:00 gcp linux TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
6:53 aws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
6:50 aws linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:19 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
6:06 gcp linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
6:05 aws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
5:38 gcp linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
5:33 aws windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:23 azure linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
5:15 gcp windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:08 gcp linux TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:06 gcp windows TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:03 aws linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:00 gcp windows TestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:57 gcp linux TestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:57 azure linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:56 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:52 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:45 gcp linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
4:43 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:42 azure windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:34 azure linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
4:30 gcp linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
4:20 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:11 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:10 azure windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
3:51 azure windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
3:48 azure linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
3:45 azure windows TestAccept

Sankalp-Mittal added a commit that referenced this pull request Aug 28, 2026
The PyDABs codegen writes databricks/bundles/<ns>/_models/*.py and each
namespace __init__.py, but those files carried no generated-file marker.

- Prepend a "# Code generated by pydabs-codegen. DO NOT EDIT." header in
  both writer paths (_write_code, _write_exports) and regenerate.
- Add a nested python/databricks/bundles/.gitattributes marking the
  generated _models trees and namespace __init__.py files as
  linguist-generated (core/ and resources/ stay hand-written).

Addresses review comments on #6335.

Co-authored-by: Isaac <no-reply@databricks.com>
iammuntazirali pushed a commit to iammuntazirali/cli that referenced this pull request Aug 29, 2026
Stacked on databricks#6393.

The PyDABs codegen (`python/codegen`) writes
`databricks/bundles/<ns>/_models/*.py` and each namespace `__init__.py`,
but those files carried no generated-file marker. This addresses review
comments on databricks#6335 asking to mark generated files as such.

## Changes

- **Header comment:** prepend `# Code generated by pydabs-codegen. DO
NOT EDIT.` in both writer paths (`_write_code`, `_write_exports`) in
`python/codegen/codegen/main.py`, then regenerate. The regeneration diff
is exactly the header (+2 lines per file) — nothing else moves.
- **`.gitattributes`:** add a nested
`python/databricks/bundles/.gitattributes` marking the generated
`_models` trees and the six generated namespace `__init__.py` files
(alerts, catalogs, jobs, pipelines, schemas, volumes) as
`linguist-generated`. `core/` and `resources/` are hand-written and stay
unmarked.

## Verification

- `git check-attr` confirms generated files are marked, hand-written
files are not.
- `codegen_tests`: 8 passed; `pydabs-test`: 220 passed.
- `ruff format` + `check` clean on `main.py`.

This pull request and its description were written by Isaac.

---------

Co-authored-by: Isaac <no-reply@databricks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants