Skip to content

Commit 38cde2c

Browse files
committed
Initial commit: Databricks wind turbine pipeline with secure credential management
- End-to-end data pipeline: Sensor → SQLite → Bacalhau → S3 → Databricks - Docker container: databricks-uploader:v1.16.0 with environment variable support - Security-first approach with credential templates and comprehensive .gitignore - EC2 spot instance deployment with Bacalhau orchestration - Full pipeline tested and operational
0 parents  commit 38cde2c

100 files changed

Lines changed: 20846 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cspell/custom-dictionary.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
ADLS
2+
apprunner
3+
asctime
4+
bacalhau
5+
bitnami
6+
buildx
7+
citus
8+
CLOB
9+
cpus
10+
creds
11+
dapi
12+
databrickscfg
13+
databricksruntime
14+
dbfs
15+
deltaio
16+
deltalake
17+
dind
18+
DISABLEANALYTICS
19+
dotenv
20+
drltab
21+
duckdb
22+
fcecd
23+
ferretdb
24+
gethostname
25+
healthcheck
26+
iloc
27+
IRUSR
28+
IWUSR
29+
Lakehouse
30+
levelname
31+
linuxcontainers
32+
makedirs
33+
minioadmin
34+
multiarch
35+
Newtonsoft
36+
nofile
37+
psql
38+
psycopg
39+
pyspark
40+
PYTHONUNBUFFERED
41+
pyyaml
42+
readwrite
43+
REARCHITECTURE
44+
referer
45+
specstory
46+
Sproc
47+
tvoc
48+
ulimits
49+
UNLOGGED
50+
venv
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Unit Testing Guide for sqlite_to_databricks_uploader.py
7+
8+
This guide outlines how to create simple unit tests for the [databricks-uploader/sqlite_to_databricks_uploader.py](mdc:databricks-uploader/sqlite_to_databricks_uploader.py) script. The goal is basic functional verification, not exhaustive testing or complex mocking.
9+
10+
## General Approach
11+
12+
1. Use the standard Python `unittest` module.
13+
2. Create a new test file, e.g., `databricks-uploader/test_uploader.py`.
14+
3. Focus on testing pure functions or functions with easily verifiable inputs and outputs.
15+
4. Avoid mocking database connections (SQLite, Databricks) or file system operations if it becomes complex. If a function is heavily reliant on I/O, consider testing its helper components or skipping it for these basic tests.
16+
17+
## Functions to Test & Example Cases
18+
19+
Here are some functions from `sqlite_to_databricks_uploader.py` that are good candidates for simple unit tests:
20+
21+
### 1. `quote_databricks_identifier(name: str) -> str`
22+
23+
* **Purpose**: Tests if identifiers are correctly quoted with backticks and if internal backticks are escaped.
24+
* **Test Cases**:
25+
* Input: `"my_table"` -> Expected Output: `"`my_table`"`
26+
* Input: `"table_with_`_backtick"` -> Expected Output: `"`table_with_``_backtick`"`
27+
* Input: `123` (as non-string) -> Expected Output: `"`123`"` (ensure it handles and logs conversion)
28+
29+
### 2. `get_qualified_table_name(db_name_for_use_stmt: str, table_name_from_config: str) -> str`
30+
31+
* **Purpose**: Tests the construction of fully qualified table names.
32+
* **Test Cases**:
33+
* `db_name_for_use_stmt="mydb"`, `table_name_from_config="mytable"` -> Expected: `"`mydb`"."`mytable`"`
34+
* `db_name_for_use_stmt="mydb"`, `table_name_from_config="myschema.mytable"` -> Expected: `"`myschema`"."`mytable`"` (and check for potential warning log if `myschema` != `mydb`)
35+
* `db_name_for_use_stmt=""`, `table_name_from_config="mytable"` -> Expect `ValueError` as per function logic.
36+
* `db_name_for_use_stmt="mydb"`, `table_name_from_config="my_schema.my_table"` -> Expected: `"`my_schema`"."`my_table`"`
37+
38+
### 3. `parse_args()`
39+
40+
* **Purpose**: Tests if command-line arguments are parsed correctly and defaults are applied.
41+
* **Test Method**:
42+
* Import the `parse_args` function.
43+
* Call `parse_args()` with a list of strings representing command-line arguments (e.g., `parse_args(['--sqlite', 'test.db', '--interval', '60'])`).
44+
* Assert that the attributes of the returned `Namespace` object have the expected values.
45+
* **Test Cases**:
46+
* Test with a minimal set of arguments.
47+
* Test overriding default values (e.g., `--interval`, `--sqlite-batch-size`).
48+
* Test boolean flags (e.g., `--once`, `--verbose`).
49+
* Test that default values are present when arguments are not supplied (e.g., `interval` should default to 30, `sqlite_batch_size` to 1000).
50+
51+
### 4. Placeholder Data Processing Functions
52+
* `sanitize_data(df: pd.DataFrame, config: dict) -> pd.DataFrame`
53+
* `filter_data(df: pd.DataFrame, config: dict) -> pd.DataFrame`
54+
* `aggregate_data(df: pd.DataFrame, config: dict) -> pd.DataFrame`
55+
* **Purpose**: Since these are placeholders, tests should just verify they return the input DataFrame.
56+
* **Test Method**: Create a sample Pandas DataFrame, pass it to these functions, and assert that the returned DataFrame is the same as the input (or an identical copy).
57+
58+
## What to Avoid (for these basic tests)
59+
60+
* Testing the `main()` function directly due to its complexity and heavy I/O.
61+
* Testing functions that heavily rely on external services or file system state unless very simple to set up (e.g., `read_data` or the main loop's SQLite/Databricks interaction parts).
62+
* Complex mocking of `sqlite3`, `databricks.sql`, `databricks.sdk`, or `yaml` modules.
63+
64+
The tests should be straightforward and confirm that the core logic of these utility and argument parsing functions behaves as expected under simple conditions.
65+
When writing the tests, import the necessary functions directly from `databricks-uploader.sqlite_to_databricks_uploader`.

.cursorindexingignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references
2+
.specstory/**

.githooks/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Git Hooks
2+
3+
This directory contains git hooks to prevent common issues in Databricks notebooks.
4+
5+
## Setup
6+
7+
To enable these hooks in your local repository, run:
8+
9+
```bash
10+
git config core.hooksPath .githooks
11+
```
12+
13+
## Hooks Included
14+
15+
### pre-commit
16+
17+
Validates Databricks notebooks before commit to check for:
18+
19+
1. **Unsupported Triggers**: Prevents use of `processingTime` or `continuous` triggers which don't work on Databricks serverless compute
20+
2. **Old Path Patterns**: Warns about nested directory structures when flat structure is preferred
21+
3. **Missing Error Handling**: Warns about streaming queries without try/except blocks
22+
4. **Hardcoded Values**: Warns about hardcoded S3 bucket names
23+
24+
## Manual Validation
25+
26+
You can manually run the validation script without committing:
27+
28+
```bash
29+
uv run -s scripts/check-databricks-notebooks.py
30+
```
31+
32+
## Bypassing Hooks (Emergency Only)
33+
34+
If you absolutely need to bypass the pre-commit hook (not recommended):
35+
36+
```bash
37+
git commit --no-verify -m "your message"
38+
```
39+
40+
## Troubleshooting
41+
42+
If the hooks aren't running:
43+
44+
1. Check that hooks path is configured:
45+
```bash
46+
git config core.hooksPath
47+
```
48+
Should output: `.githooks`
49+
50+
2. Ensure hooks are executable:
51+
```bash
52+
chmod +x .githooks/pre-commit
53+
```
54+
55+
3. Verify uv is installed for Python scripts:
56+
```bash
57+
curl -LsSf https://astral.sh/uv/install.sh | sh
58+
```

.githooks/pre-commit

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
# Pre-commit hook to validate Python code
3+
4+
set -e
5+
6+
# Check if we have any Python changes
7+
if git diff --cached --name-only | grep -q "\.py$"; then
8+
echo "Running validation on Python files..."
9+
10+
# Run comprehensive validation
11+
if command -v uv &> /dev/null; then
12+
uv run -s scripts/validate-all.py check
13+
14+
if [ $? -ne 0 ]; then
15+
echo ""
16+
echo "❌ Validation failed!"
17+
echo "Fix the issues above before committing."
18+
echo ""
19+
echo "To auto-fix some issues, run:"
20+
echo " uv run -s scripts/validate-all.py fix"
21+
exit 1
22+
fi
23+
else
24+
echo "⚠️ uv not found. Install it with:"
25+
echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
26+
exit 1
27+
fi
28+
fi
29+
30+
echo "✅ All pre-commit checks passed!"
31+
exit 0

.githooks/pre-commit-databricks

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/bash
2+
3+
# Pre-commit hook to validate Databricks notebooks before committing
4+
# Install by running: ln -sf ../../.githooks/pre-commit-databricks .git/hooks/pre-commit
5+
6+
set -e
7+
8+
# Colors for output
9+
RED='\033[0;31m'
10+
GREEN='\033[0;32m'
11+
YELLOW='\033[1;33m'
12+
NC='\033[0m' # No Color
13+
14+
echo -e "${YELLOW}Running Databricks notebook validation...${NC}"
15+
16+
# Find all changed .py files in databricks-notebooks directory
17+
NOTEBOOKS=$(git diff --cached --name-only --diff-filter=ACM | \
18+
grep -E '^databricks-notebooks/.*\.py$' || true)
19+
20+
if [ -z "$NOTEBOOKS" ]; then
21+
echo -e "${GREEN}No Databricks notebooks to validate${NC}"
22+
exit 0
23+
fi
24+
25+
# Check if validation script exists
26+
VALIDATOR="scripts/validate-databricks-notebook.py"
27+
if [ ! -f "$VALIDATOR" ]; then
28+
echo -e "${YELLOW}Warning: Validator script not found at $VALIDATOR${NC}"
29+
echo -e "${YELLOW}Skipping notebook validation${NC}"
30+
exit 0
31+
fi
32+
33+
# Validate each notebook
34+
FAILED=0
35+
for notebook in $NOTEBOOKS; do
36+
echo -n "Validating $notebook... "
37+
38+
if uv run -s "$VALIDATOR" "$notebook" --quiet; then
39+
echo -e "${GREEN}${NC}"
40+
else
41+
echo -e "${RED}${NC}"
42+
echo -e "${RED}Validation failed for $notebook${NC}"
43+
echo "Run the following command for details:"
44+
echo " uv run -s $VALIDATOR $notebook --show-context"
45+
FAILED=1
46+
fi
47+
done
48+
49+
if [ $FAILED -eq 1 ]; then
50+
echo -e "${RED}Commit aborted due to validation errors${NC}"
51+
echo "Fix the errors or use 'git commit --no-verify' to skip validation"
52+
exit 1
53+
fi
54+
55+
echo -e "${GREEN}All notebooks validated successfully${NC}"
56+
exit 0
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Validate Databricks Notebooks
2+
3+
on:
4+
push:
5+
paths:
6+
- 'databricks-notebooks/**/*.py'
7+
- 'scripts/validate-databricks-notebook.py'
8+
pull_request:
9+
paths:
10+
- 'databricks-notebooks/**/*.py'
11+
- 'scripts/validate-databricks-notebook.py'
12+
13+
jobs:
14+
validate:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: '3.11'
24+
25+
- name: Install uv
26+
run: |
27+
curl -LsSf https://astral.sh/uv/install.sh | sh
28+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
29+
30+
- name: Find Databricks notebooks
31+
id: find-notebooks
32+
run: |
33+
notebooks=$(find databricks-notebooks -name "*.py" -type f | tr '\n' ' ')
34+
echo "notebooks=$notebooks" >> $GITHUB_OUTPUT
35+
echo "Found notebooks: $notebooks"
36+
37+
- name: Validate notebooks
38+
if: steps.find-notebooks.outputs.notebooks != ''
39+
run: |
40+
for notebook in ${{ steps.find-notebooks.outputs.notebooks }}; do
41+
echo "Validating $notebook..."
42+
uv run -s scripts/validate-databricks-notebook.py "$notebook" \
43+
--show-context || exit 1
44+
done
45+
46+
- name: Run strict validation
47+
if: github.event_name == 'pull_request'
48+
run: |
49+
for notebook in ${{ steps.find-notebooks.outputs.notebooks }}; do
50+
echo "Running strict validation on $notebook..."
51+
uv run -s scripts/validate-databricks-notebook.py "$notebook" \
52+
--strict --show-context || exit 1
53+
done

0 commit comments

Comments
 (0)