demo_uc_setup 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- demo_uc_setup/__init__.py +0 -0
- demo_uc_setup/common.py +49 -0
- demo_uc_setup/config.py +23 -0
- demo_uc_setup/unity_catalog_setup.py +82 -0
- demo_uc_setup/unity_catalog_teardown.py +28 -0
- demo_uc_setup-0.1.0.dist-info/METADATA +105 -0
- demo_uc_setup-0.1.0.dist-info/RECORD +8 -0
- demo_uc_setup-0.1.0.dist-info/WHEEL +4 -0
File without changes
|
demo_uc_setup/common.py
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import TypeVar, Generic
|
3
|
+
from databricks.sdk import WorkspaceClient
|
4
|
+
|
5
|
+
from demo_uc_setup.config import Config
|
6
|
+
|
7
|
+
# Example of a type variable bound to our Config class
|
8
|
+
T = TypeVar("T", bound=Config)
|
9
|
+
|
10
|
+
class Task(Generic[T]):
|
11
|
+
"""
|
12
|
+
A reusable Task base class that works both locally and in Databricks notebooks.
|
13
|
+
When running locally, requires databricks_host and databricks_token.
|
14
|
+
When running in a notebook, these parameters are optional.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, config_class: type[T]):
|
18
|
+
# Instantiate the typed configuration
|
19
|
+
self.config: T = config_class()
|
20
|
+
# Setup a basic logger
|
21
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
22
|
+
logging.basicConfig(level=logging.INFO)
|
23
|
+
|
24
|
+
# Create a Databricks workspace client with or without credentials
|
25
|
+
if self.config.databricks_host and self.config.databricks_token:
|
26
|
+
# Local execution with credentials
|
27
|
+
self.workspace_client = WorkspaceClient(
|
28
|
+
host=self.config.databricks_host,
|
29
|
+
token=self.config.databricks_token
|
30
|
+
)
|
31
|
+
else:
|
32
|
+
# Notebook execution - no credentials needed
|
33
|
+
self.workspace_client = WorkspaceClient()
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def entrypoint(cls, *args, **kwargs):
|
37
|
+
"""
|
38
|
+
Creates an instance of the task and runs it. If you
|
39
|
+
want a consistent run pattern, place it here.
|
40
|
+
"""
|
41
|
+
instance = cls(*args, **kwargs)
|
42
|
+
instance.run()
|
43
|
+
|
44
|
+
def run(self):
|
45
|
+
"""
|
46
|
+
The main entrypoint for the task's execution.
|
47
|
+
Override this in subclasses to implement custom logic.
|
48
|
+
"""
|
49
|
+
self.logger.info("Base Task run method. Override in subclasses.")
|
demo_uc_setup/config.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
from pydantic_settings import BaseSettings
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
class Config(BaseSettings):
|
5
|
+
"""
|
6
|
+
Configuration class using Pydantic BaseSettings.
|
7
|
+
By default, each field can be overridden by environment
|
8
|
+
variables matching the field name (in uppercase).
|
9
|
+
For example, DATABRICKS_HOST, DATABRICKS_TOKEN, etc.
|
10
|
+
"""
|
11
|
+
|
12
|
+
# Databricks connection settings - optional for notebook execution
|
13
|
+
databricks_host: Optional[str] = None
|
14
|
+
databricks_token: Optional[str] = None
|
15
|
+
|
16
|
+
# Default names for Unity Catalog demo objects
|
17
|
+
demo_catalog_name: str = "demo_catalog"
|
18
|
+
demo_schemas: list[str] = ["demo_schema_1", "demo_schema_2"] # List of schemas
|
19
|
+
demo_volume_name: str = "demo_volume" # This could also be a list if needed
|
20
|
+
|
21
|
+
class Config:
|
22
|
+
env_file = ".env" # or any custom file, if desired
|
23
|
+
env_file_encoding = "utf-8"
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from typing import Type
|
2
|
+
from databricks.sdk.service import catalog
|
3
|
+
from demo_uc_setup.common import Task, T
|
4
|
+
from demo_uc_setup.config import Config
|
5
|
+
|
6
|
+
class UnityCatalogSetupTask(Task[Config]):
|
7
|
+
"""
|
8
|
+
A task to ensure catalogs, schemas, and volumes exist
|
9
|
+
in the Databricks workspace. Uses typed config for
|
10
|
+
resource names, credentials, etc.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, config_class: Type[T] = Config):
|
14
|
+
super().__init__(config_class)
|
15
|
+
|
16
|
+
def run(self):
|
17
|
+
self.logger.info("Starting Unity Catalog setup...")
|
18
|
+
|
19
|
+
# 1) Ensure the catalog exists
|
20
|
+
catalog_name = self.config.demo_catalog_name
|
21
|
+
self.logger.info(f"Ensuring catalog '{catalog_name}'")
|
22
|
+
try:
|
23
|
+
self.workspace_client.catalogs.get(name=catalog_name)
|
24
|
+
self.logger.info(f"Catalog '{catalog_name}' already exists.")
|
25
|
+
except Exception:
|
26
|
+
self.logger.info(f"Catalog '{catalog_name}' not found; creating it.")
|
27
|
+
self.workspace_client.catalogs.create(
|
28
|
+
name=catalog_name,
|
29
|
+
comment="Demo Catalog for Databricks demos"
|
30
|
+
)
|
31
|
+
|
32
|
+
# 2) Ensure all schemas exist and create volumes within each schema
|
33
|
+
for schema_name in self.config.demo_schemas:
|
34
|
+
# Create schema
|
35
|
+
self.logger.info(f"Ensuring schema '{catalog_name}.{schema_name}'")
|
36
|
+
try:
|
37
|
+
self.workspace_client.schemas.get(
|
38
|
+
name=schema_name,
|
39
|
+
catalog_name=catalog_name
|
40
|
+
)
|
41
|
+
self.logger.info(f"Schema '{catalog_name}.{schema_name}' already exists.")
|
42
|
+
except Exception:
|
43
|
+
try:
|
44
|
+
self.logger.info(f"Schema '{catalog_name}.{schema_name}' not found; creating it.")
|
45
|
+
self.workspace_client.schemas.create(
|
46
|
+
name=schema_name,
|
47
|
+
catalog_name=catalog_name,
|
48
|
+
comment=f"Demo Schema {schema_name} for Databricks demos"
|
49
|
+
)
|
50
|
+
except Exception as e:
|
51
|
+
if "already exists" in str(e):
|
52
|
+
self.logger.info(f"Schema '{catalog_name}.{schema_name}' already exists (caught during creation).")
|
53
|
+
else:
|
54
|
+
raise e
|
55
|
+
|
56
|
+
# Create volume within this schema
|
57
|
+
volume_name = self.config.demo_volume_name
|
58
|
+
self.logger.info(f"Ensuring volume '{catalog_name}.{schema_name}.{volume_name}'")
|
59
|
+
try:
|
60
|
+
self.workspace_client.volumes.get(
|
61
|
+
name=volume_name,
|
62
|
+
catalog_name=catalog_name,
|
63
|
+
schema_name=schema_name
|
64
|
+
)
|
65
|
+
self.logger.info(f"Volume '{catalog_name}.{schema_name}.{volume_name}' already exists.")
|
66
|
+
except Exception:
|
67
|
+
try:
|
68
|
+
self.logger.info(f"Volume '{catalog_name}.{schema_name}.{volume_name}' not found; creating it.")
|
69
|
+
self.workspace_client.volumes.create(
|
70
|
+
name=volume_name,
|
71
|
+
catalog_name=catalog_name,
|
72
|
+
schema_name=schema_name,
|
73
|
+
volume_type=catalog.VolumeType.MANAGED,
|
74
|
+
comment=f"Demo Volume for schema {schema_name}"
|
75
|
+
)
|
76
|
+
except Exception as e:
|
77
|
+
if "already exists" in str(e):
|
78
|
+
self.logger.info(f"Volume '{catalog_name}.{schema_name}.{volume_name}' already exists (caught during creation).")
|
79
|
+
else:
|
80
|
+
raise e
|
81
|
+
|
82
|
+
self.logger.info("Unity Catalog setup complete!")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import Type
|
2
|
+
from databricks.sdk import WorkspaceClient
|
3
|
+
from demo_uc_setup.common import Task, T
|
4
|
+
from demo_uc_setup.config import Config
|
5
|
+
|
6
|
+
class UnityCatalogTeardownTask(Task[Config]):
|
7
|
+
"""
|
8
|
+
A task to delete (teardown) the Unity Catalog resources in
|
9
|
+
the configured Databricks workspace. Uses typed config for
|
10
|
+
resource names, credentials, etc.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, config_class: Type[T] = Config):
|
14
|
+
super().__init__(config_class)
|
15
|
+
|
16
|
+
def run(self):
|
17
|
+
self.logger.info("Starting teardown of Unity Catalog resources...")
|
18
|
+
|
19
|
+
catalog_name = self.config.demo_catalog_name
|
20
|
+
self.logger.info(f"Deleting catalog '{catalog_name}' and its dependencies (force=True).")
|
21
|
+
|
22
|
+
try:
|
23
|
+
self.workspace_client.catalogs.delete(name=catalog_name, force=True)
|
24
|
+
self.logger.info(f"Catalog '{catalog_name}' (and its contents) successfully deleted.")
|
25
|
+
except Exception as e:
|
26
|
+
self.logger.error(f"Failed to delete catalog '{catalog_name}'. Reason: {e}")
|
27
|
+
|
28
|
+
self.logger.info("Unity Catalog teardown complete!")
|
@@ -0,0 +1,105 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: demo_uc_setup
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A reusable task-based framework for managing Unity Catalog in Databricks
|
5
|
+
Requires-Python: >=3.11
|
6
|
+
Requires-Dist: databricks-connect>=16.1.1
|
7
|
+
Requires-Dist: databricks-sdk>=0.44.1
|
8
|
+
Requires-Dist: pydantic-settings>=2.8.0
|
9
|
+
Requires-Dist: pydantic>=2.10.6
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
|
12
|
+
# Databricks Unity Catalog Setup Demo
|
13
|
+
|
14
|
+
A Python package that demonstrates automated setup and teardown of Databricks Unity Catalog resources using the Databricks SDK. This package provides a reusable framework for managing Unity Catalog resources programmatically, both from local environments and within Databricks notebooks.
|
15
|
+
|
16
|
+
## Features
|
17
|
+
|
18
|
+
- Automated creation of Unity Catalog resources:
|
19
|
+
- Catalogs
|
20
|
+
- Schemas
|
21
|
+
- Volumes
|
22
|
+
- Configurable resource naming via environment variables
|
23
|
+
- Support for both local execution and Databricks notebook execution
|
24
|
+
- Type-safe configuration management using Pydantic
|
25
|
+
- Clean teardown functionality
|
26
|
+
|
27
|
+
## Prerequisites
|
28
|
+
|
29
|
+
- Python 3.8+
|
30
|
+
- A Databricks workspace with Unity Catalog enabled
|
31
|
+
- Appropriate permissions to create/manage Unity Catalog resources
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
```bash
|
36
|
+
pip install demo-uc-setup
|
37
|
+
```
|
38
|
+
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
The package uses environment variables for configuration. You can set these either in your environment or in a `.env` file:
|
42
|
+
|
43
|
+
```env
|
44
|
+
# Required for local execution (optional in Databricks notebooks)
|
45
|
+
DATABRICKS_HOST=your-workspace-url
|
46
|
+
DATABRICKS_TOKEN=your-pat-token
|
47
|
+
|
48
|
+
# Optional - override default resource names
|
49
|
+
DEMO_CATALOG_NAME=custom_catalog_name
|
50
|
+
DEMO_SCHEMAS=["schema1", "schema2"]
|
51
|
+
DEMO_VOLUME_NAME=custom_volume_name
|
52
|
+
```
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
### Local Execution
|
57
|
+
|
58
|
+
```python
|
59
|
+
from demo_uc_setup.unity_catalog_setup import UnityCatalogSetupTask
|
60
|
+
from demo_uc_setup.unity_catalog_teardown import UnityCatalogTeardownTask
|
61
|
+
|
62
|
+
# Setup Unity Catalog resources
|
63
|
+
UnityCatalogSetupTask.entrypoint()
|
64
|
+
|
65
|
+
# Teardown Unity Catalog resources
|
66
|
+
UnityCatalogTeardownTask.entrypoint()
|
67
|
+
```
|
68
|
+
|
69
|
+
### Databricks Notebook Execution
|
70
|
+
|
71
|
+
```python
|
72
|
+
%pip install demo-uc-setup
|
73
|
+
|
74
|
+
from demo_uc_setup.unity_catalog_setup import UnityCatalogSetupTask
|
75
|
+
UnityCatalogSetupTask.entrypoint()
|
76
|
+
```
|
77
|
+
|
78
|
+
## Default Resource Names
|
79
|
+
|
80
|
+
If not overridden via environment variables, the package will create:
|
81
|
+
- A catalog named `demo_catalog`
|
82
|
+
- Two schemas: `demo_schema_1` and `demo_schema_2`
|
83
|
+
- A volume named `demo_volume` in each schema
|
84
|
+
|
85
|
+
## Extending the Framework
|
86
|
+
|
87
|
+
The package provides a reusable `Task` base class that can be extended for custom Unity Catalog operations:
|
88
|
+
|
89
|
+
```python
|
90
|
+
from demo_uc_setup.common import Task
|
91
|
+
from demo_uc_setup.config import Config
|
92
|
+
|
93
|
+
class CustomTask(Task[Config]):
|
94
|
+
def run(self):
|
95
|
+
self.logger.info("Starting custom task...")
|
96
|
+
# Your custom logic here
|
97
|
+
```
|
98
|
+
|
99
|
+
## Contributing
|
100
|
+
|
101
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
102
|
+
|
103
|
+
## License
|
104
|
+
|
105
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
demo_uc_setup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
demo_uc_setup/common.py,sha256=r5v-nUh7EoGsR4ecQi-pA9NJVRyJlRgOtc_9JxQWjxs,1754
|
3
|
+
demo_uc_setup/config.py,sha256=Jwl5anXQ6Ceq-hRyjZ9gUtBaC3iqqJXZCkLjNYjny1o,873
|
4
|
+
demo_uc_setup/unity_catalog_setup.py,sha256=3jfIlS9_nxJpXcoGbXJ2Pj4EefqAlpo-GTV8MShoa1w,3680
|
5
|
+
demo_uc_setup/unity_catalog_teardown.py,sha256=jHcT4iWeyUIlFDhvUGSfTW2RAYL7d-0sW4VU_uLuRd4,1095
|
6
|
+
demo_uc_setup-0.1.0.dist-info/METADATA,sha256=aVkYiBtNuljFYng3CL2JPMLbzkBupHhFYMRiEBDLRjc,2934
|
7
|
+
demo_uc_setup-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
+
demo_uc_setup-0.1.0.dist-info/RECORD,,
|