bauta 0.1.0__tar.gz
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.
- bauta-0.1.0/LICENSE +21 -0
- bauta-0.1.0/PKG-INFO +211 -0
- bauta-0.1.0/README.md +156 -0
- bauta-0.1.0/bauta/__init__.py +93 -0
- bauta-0.1.0/bauta/audit.py +319 -0
- bauta-0.1.0/bauta/builtinTransforms.py +373 -0
- bauta-0.1.0/bauta/cli.py +1106 -0
- bauta-0.1.0/bauta/configuration.py +521 -0
- bauta-0.1.0/bauta/database.py +396 -0
- bauta-0.1.0/bauta/databaseDialects.py +1153 -0
- bauta-0.1.0/bauta/dependencyGraph.py +127 -0
- bauta-0.1.0/bauta/discovery.py +402 -0
- bauta-0.1.0/bauta/fpe.py +118 -0
- bauta-0.1.0/bauta/log.py +218 -0
- bauta-0.1.0/bauta/masking.py +1704 -0
- bauta-0.1.0/bauta/memory.py +357 -0
- bauta-0.1.0/bauta/py.typed +0 -0
- bauta-0.1.0/bauta/reporting.py +358 -0
- bauta-0.1.0/bauta/runner.py +762 -0
- bauta-0.1.0/bauta/schema.py +408 -0
- bauta-0.1.0/bauta/scrubbing.py +115 -0
- bauta-0.1.0/bauta/subset.py +289 -0
- bauta-0.1.0/bauta/synthesize.py +428 -0
- bauta-0.1.0/bauta/transform.py +164 -0
- bauta-0.1.0/bauta.egg-info/PKG-INFO +211 -0
- bauta-0.1.0/bauta.egg-info/SOURCES.txt +74 -0
- bauta-0.1.0/bauta.egg-info/dependency_links.txt +1 -0
- bauta-0.1.0/bauta.egg-info/entry_points.txt +2 -0
- bauta-0.1.0/bauta.egg-info/requires.txt +32 -0
- bauta-0.1.0/bauta.egg-info/top_level.txt +1 -0
- bauta-0.1.0/pyproject.toml +87 -0
- bauta-0.1.0/setup.cfg +4 -0
- bauta-0.1.0/tests/test_audit.py +252 -0
- bauta-0.1.0/tests/test_builtinTransforms.py +156 -0
- bauta-0.1.0/tests/test_cli.py +871 -0
- bauta-0.1.0/tests/test_configuration.py +333 -0
- bauta-0.1.0/tests/test_database.py +393 -0
- bauta-0.1.0/tests/test_databaseDialects.py +451 -0
- bauta-0.1.0/tests/test_dependencyGraph.py +216 -0
- bauta-0.1.0/tests/test_discovery.py +181 -0
- bauta-0.1.0/tests/test_documentation.py +114 -0
- bauta-0.1.0/tests/test_environmentVariables.py +182 -0
- bauta-0.1.0/tests/test_fpe.py +52 -0
- bauta-0.1.0/tests/test_incremental_demo.py +106 -0
- bauta-0.1.0/tests/test_integration_connections.py +125 -0
- bauta-0.1.0/tests/test_integration_cross_database.py +103 -0
- bauta-0.1.0/tests/test_integration_keys.py +243 -0
- bauta-0.1.0/tests/test_integration_mariadb.py +203 -0
- bauta-0.1.0/tests/test_integration_masking.py +248 -0
- bauta-0.1.0/tests/test_integration_mssql.py +353 -0
- bauta-0.1.0/tests/test_integration_mysql.py +304 -0
- bauta-0.1.0/tests/test_integration_oracle.py +299 -0
- bauta-0.1.0/tests/test_integration_postgresql.py +380 -0
- bauta-0.1.0/tests/test_integration_schema.py +187 -0
- bauta-0.1.0/tests/test_integration_scrubbing.py +136 -0
- bauta-0.1.0/tests/test_integration_sqlite.py +281 -0
- bauta-0.1.0/tests/test_integration_synthesize.py +70 -0
- bauta-0.1.0/tests/test_lazy_driver_imports.py +39 -0
- bauta-0.1.0/tests/test_log.py +199 -0
- bauta-0.1.0/tests/test_maskVectors.py +58 -0
- bauta-0.1.0/tests/test_masking.py +1055 -0
- bauta-0.1.0/tests/test_masking_demo.py +84 -0
- bauta-0.1.0/tests/test_masking_end_to_end.py +195 -0
- bauta-0.1.0/tests/test_memory.py +270 -0
- bauta-0.1.0/tests/test_nativeMasking.py +145 -0
- bauta-0.1.0/tests/test_native_masking_demo.py +65 -0
- bauta-0.1.0/tests/test_packaging.py +73 -0
- bauta-0.1.0/tests/test_reporting.py +183 -0
- bauta-0.1.0/tests/test_runner.py +1520 -0
- bauta-0.1.0/tests/test_schema.py +274 -0
- bauta-0.1.0/tests/test_scrubbing.py +213 -0
- bauta-0.1.0/tests/test_shipped_example_configuration.py +132 -0
- bauta-0.1.0/tests/test_subset.py +254 -0
- bauta-0.1.0/tests/test_synthesize.py +147 -0
- bauta-0.1.0/tests/test_transform.py +158 -0
- bauta-0.1.0/tests/test_walkthrough.py +83 -0
bauta-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Ribeiro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
bauta-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bauta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Safe, realistic copies of production across databases: masking, subsets, synthetic data, and the ETL to move them
|
|
5
|
+
Author: David Ribeiro
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ribeiro11075/bauta
|
|
8
|
+
Project-URL: Documentation, https://github.com/ribeiro11075/bauta/tree/master/docs
|
|
9
|
+
Project-URL: Issues, https://github.com/ribeiro11075/bauta/issues
|
|
10
|
+
Keywords: data masking,test data,anonymization,pseudonymization,database subsetting,synthetic data,etl,format-preserving encryption,oracle,sql server,postgresql,mysql
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Security
|
|
26
|
+
Classifier: Topic :: Software Development :: Testing
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: pyyaml<7,>=6.0.1
|
|
32
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
33
|
+
Provides-Extra: mysql
|
|
34
|
+
Requires-Dist: mysql-connector-python>=8.4; extra == "mysql"
|
|
35
|
+
Provides-Extra: postgresql
|
|
36
|
+
Requires-Dist: psycopg2>=2.9.6; extra == "postgresql"
|
|
37
|
+
Provides-Extra: oracle
|
|
38
|
+
Requires-Dist: oracledb>=2.5; extra == "oracle"
|
|
39
|
+
Provides-Extra: mssql
|
|
40
|
+
Requires-Dist: pymssql>=2.3.5; extra == "mssql"
|
|
41
|
+
Provides-Extra: mariadb
|
|
42
|
+
Requires-Dist: mysql-connector-python>=8.4; extra == "mariadb"
|
|
43
|
+
Provides-Extra: sqlite
|
|
44
|
+
Provides-Extra: fpe
|
|
45
|
+
Requires-Dist: cryptography>=42; extra == "fpe"
|
|
46
|
+
Provides-Extra: all
|
|
47
|
+
Requires-Dist: bauta[fpe,mariadb,mssql,mysql,oracle,postgresql,sqlite]; extra == "all"
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: pytest==9.1.1; extra == "dev"
|
|
50
|
+
Requires-Dist: mypy==2.3.1; extra == "dev"
|
|
51
|
+
Requires-Dist: types-PyYAML==6.0.12.20260815; extra == "dev"
|
|
52
|
+
Requires-Dist: build==1.6.1; extra == "dev"
|
|
53
|
+
Requires-Dist: twine==7.0.0; extra == "dev"
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
# Bauta
|
|
57
|
+
|
|
58
|
+
**Put a mask on production:** a safe, realistic stand-in for your production data, in whichever database you need it. Bauta masks what it copies, copies only the slice you need with every relationship intact, generates what may not be copied at all, and moves data between databases on a schedule you already run, with nothing to host.
|
|
59
|
+
|
|
60
|
+
- **Masking:** consistent across tables and runs, one-to-one for keys (NIST FF1 where policy requires it), applied before anything reaches the target, and every column must be covered.
|
|
61
|
+
- **Discovery, subsets and synthetic data:** propose a masking policy from a live schema, copy a referentially complete slice of production, create the copy's tables in whichever database it goes to, and fill tables that can't be copied with generated rows.
|
|
62
|
+
- **Audit:** report what every job does with data and what a reviewer should question, and seal each run's masking manifest so it can be verified later.
|
|
63
|
+
- **Six databases:** Oracle, SQL Server, PostgreSQL, MySQL, MariaDB and SQLite, as source or target in any combination.
|
|
64
|
+
- **Streaming:** memory stays flat however large the table, and PostgreSQL and SQL Server targets load in bulk.
|
|
65
|
+
- **Fast:** ten million rows of six masked columns in under two minutes on one core with the optional native masker, and under eight without. Either way the masks are the same.
|
|
66
|
+
- **Incremental loads:** extract only what changed since the last successful run.
|
|
67
|
+
- **A dependency graph:** jobs run in order, concurrently where they can, each in its own process with an optional timeout.
|
|
68
|
+
- **Operable:** run history, Prometheus metrics, webhook alerts, run state in a file or a table, and passwords from a command for cloud IAM tokens.
|
|
69
|
+
- **No infrastructure:** a `pip install`, some YAML, and a command you run from cron.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
Python 3.10 or newer. Choose the drivers you need as extras; each is loaded only when a connection uses it.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
pip install "bauta[postgresql,oracle]"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
It isn't on PyPI yet. Until the first release, install from a clone:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
git clone https://github.com/ribeiro11075/bauta.git
|
|
84
|
+
cd bauta
|
|
85
|
+
pip install -e ".[postgresql,oracle]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| Extra | Driver | Needs besides pip |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `mysql`, `mariadb` | mysql-connector-python | nothing |
|
|
91
|
+
| `postgresql` | psycopg2, built from source | a C compiler and PostgreSQL's client library (below) |
|
|
92
|
+
| `oracle` | oracledb, in thin mode | nothing — no Oracle client |
|
|
93
|
+
| `mssql` | pymssql | nothing |
|
|
94
|
+
| `sqlite` | Python's own `sqlite3` | nothing |
|
|
95
|
+
| `fpe` | cryptography, for the `fpe` masking strategy | nothing; `oracle` already brings it |
|
|
96
|
+
| `all` | every driver above | as for `postgresql` |
|
|
97
|
+
|
|
98
|
+
**Building psycopg2.** pip compiles it, so it needs a compiler and `pg_config`:
|
|
99
|
+
|
|
100
|
+
- macOS: `xcode-select --install`, then `brew install libpq` and `export PATH="$(brew --prefix libpq)/bin:$PATH"` (Homebrew doesn't put libpq on the path by itself).
|
|
101
|
+
- Debian or Ubuntu: `apt install build-essential libpq-dev`.
|
|
102
|
+
|
|
103
|
+
To skip the build, leave `postgresql` (and `all`) out and install the prebuilt driver beside the other extras: `pip install "bauta[mysql,oracle,mssql]" psycopg2-binary`. psycopg2's maintainers recommend the source build for production.
|
|
104
|
+
|
|
105
|
+
**The native masker (optional).** `bauta-rs`, in `mask-rs/`, masks in Rust: four to five times the throughput, identical masks, nothing to configure. With [Rust](https://rustup.rs) 1.83 or newer, pip builds it in the same command as the rest:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pip install -e ".[all]" ./mask-rs/py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Without Rust, leave `./mask-rs/py` off; everything works, only slower. See [the native masker](docs/masking.md#the-native-masker).
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
## Quickstart
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
mkdir configuration
|
|
118
|
+
cp example/starter/configuration/*.yaml configuration/
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Edit `configuration/database.yaml` and `configuration/jobs.yaml` for your databases, then supply the credentials they reference:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
export SOURCE_DB_PASSWORD=... TARGET_DB_PASSWORD=... MASKING_KEY=...
|
|
125
|
+
|
|
126
|
+
bauta validate # check the configuration, offline
|
|
127
|
+
bauta run --dry-run # check connections and tables, moving nothing
|
|
128
|
+
bauta run # run every job once
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
To see it work without any of that, using throwaway SQLite databases:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
python example/walkthrough/demo.py # the whole workflow: discover, subset, audit, mask, verify, synthesize
|
|
135
|
+
python example/incremental/demo.py # streaming and incremental loads
|
|
136
|
+
python example/masking/demo.py # masking, discovery and a subset, from Python
|
|
137
|
+
python example/native-masking/demo.py # the same job masked in Python and in Rust, compared
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## The command
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
bauta run run data jobs once, masking any with a `masking` section
|
|
145
|
+
bauta validate check configuration without connecting
|
|
146
|
+
bauta jobs show the job graph and what's due
|
|
147
|
+
bauta history show recent job outcomes recorded with --history
|
|
148
|
+
|
|
149
|
+
bauta discover propose a masking policy for tables
|
|
150
|
+
bauta subset generate jobs that copy a referentially complete subset
|
|
151
|
+
bauta schema create target tables from source ones, in the target's dialect
|
|
152
|
+
bauta synthesize fill tables with generated rows, for data that can't be copied
|
|
153
|
+
bauta clear empty the target tables of jobs, children first
|
|
154
|
+
|
|
155
|
+
bauta audit report what each job does with data, and what to question
|
|
156
|
+
bauta verify-manifest check a manifest is unaltered, and who signed it
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Exit code | Meaning |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| `0` | every job completed |
|
|
162
|
+
| `1` | a job failed, or was skipped because a predecessor failed, or the command failed on a database error |
|
|
163
|
+
| `2` | invalid configuration or usage |
|
|
164
|
+
| `130` | interrupted by a signal: running jobs finished, the rest were skipped |
|
|
165
|
+
|
|
166
|
+
`run` makes one pass and exits, so it fits under cron or a Kubernetes CronJob. A second `run` sharing the same run state refuses to start while the first is still going. The useful flags:
|
|
167
|
+
|
|
168
|
+
| Flag | |
|
|
169
|
+
| --- | --- |
|
|
170
|
+
| `--config DIR` | where the YAML lives; default `./configuration` |
|
|
171
|
+
| `--job NAME` | run only this job — without its predecessors, which it warns about |
|
|
172
|
+
| `--force` | ignore `refresh` windows |
|
|
173
|
+
| `--forever` | stay running; for freshness under a minute |
|
|
174
|
+
| `--log-format json` | structured logs for a collector |
|
|
175
|
+
| `--log FILE` | also log to a file, in addition to stderr (`--quiet` silences stderr) |
|
|
176
|
+
| `--memory FILE` | where run state (last runs, watermarks) is kept, overriding `jobs.yaml`'s [`memory`](docs/configuration.md#file-level) |
|
|
177
|
+
| `--memory-database ALIAS` | keep run state in a database table instead |
|
|
178
|
+
| `--history FILE` | append each job's outcome to a JSON-lines history |
|
|
179
|
+
| `--metrics FILE` | write Prometheus metrics for the textfile collector (`--metrics-push URL` for a Pushgateway) |
|
|
180
|
+
| `--notify-url URL` | post to a webhook when a run doesn't succeed; default `$BAUTA_NOTIFY_URL` |
|
|
181
|
+
| `--accept-key-change` | run upsert jobs whose masking key changed since their last run |
|
|
182
|
+
| `--manifest FILE` | write a sealed JSON record of what was masked, and how; signed if `$BAUTA_MANIFEST_KEY` is set |
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
## Documentation
|
|
186
|
+
|
|
187
|
+
| | |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| [Configuration](docs/configuration.md) | every field, how credentials are read from the environment, and connection options such as TLS |
|
|
190
|
+
| [Masking](docs/masking.md) | strategies, consistent masks across tables, the key, the manifest, `audit`, `discover`, `subset`, `schema`, `synthesize` and `clear` |
|
|
191
|
+
| [How it works](docs/design.md) | streaming, incremental loads, retries, scheduling, and the masking design |
|
|
192
|
+
| [Operating it](docs/operations.md) | run state, history, metrics and notifications |
|
|
193
|
+
| [Security model](docs/security.md) | what masking protects and what it doesn't, the constructions, keys, and a deployment checklist |
|
|
194
|
+
| [Library](docs/library.md) | embedding it in Python, results, memory backends |
|
|
195
|
+
| [Development](docs/development.md) | running the tests, including against real databases |
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
## Layout
|
|
199
|
+
|
|
200
|
+
| | |
|
|
201
|
+
| --- | --- |
|
|
202
|
+
| `bauta/` | the package; `runner.py` runs jobs, `masking.py` masks, `databaseDialects.py` holds per-database SQL |
|
|
203
|
+
| `mask-rs/` | the optional native masker, in Rust — see [its README](mask-rs/README.md) |
|
|
204
|
+
| `example/` | runnable demos, each with its `configuration/`, and a starter configuration — see [its README](example/README.md) |
|
|
205
|
+
| `docs/` | the documentation above |
|
|
206
|
+
| `tests/` | the test suite |
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
[MIT](LICENSE)
|
bauta-0.1.0/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Bauta
|
|
2
|
+
|
|
3
|
+
**Put a mask on production:** a safe, realistic stand-in for your production data, in whichever database you need it. Bauta masks what it copies, copies only the slice you need with every relationship intact, generates what may not be copied at all, and moves data between databases on a schedule you already run, with nothing to host.
|
|
4
|
+
|
|
5
|
+
- **Masking:** consistent across tables and runs, one-to-one for keys (NIST FF1 where policy requires it), applied before anything reaches the target, and every column must be covered.
|
|
6
|
+
- **Discovery, subsets and synthetic data:** propose a masking policy from a live schema, copy a referentially complete slice of production, create the copy's tables in whichever database it goes to, and fill tables that can't be copied with generated rows.
|
|
7
|
+
- **Audit:** report what every job does with data and what a reviewer should question, and seal each run's masking manifest so it can be verified later.
|
|
8
|
+
- **Six databases:** Oracle, SQL Server, PostgreSQL, MySQL, MariaDB and SQLite, as source or target in any combination.
|
|
9
|
+
- **Streaming:** memory stays flat however large the table, and PostgreSQL and SQL Server targets load in bulk.
|
|
10
|
+
- **Fast:** ten million rows of six masked columns in under two minutes on one core with the optional native masker, and under eight without. Either way the masks are the same.
|
|
11
|
+
- **Incremental loads:** extract only what changed since the last successful run.
|
|
12
|
+
- **A dependency graph:** jobs run in order, concurrently where they can, each in its own process with an optional timeout.
|
|
13
|
+
- **Operable:** run history, Prometheus metrics, webhook alerts, run state in a file or a table, and passwords from a command for cloud IAM tokens.
|
|
14
|
+
- **No infrastructure:** a `pip install`, some YAML, and a command you run from cron.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Python 3.10 or newer. Choose the drivers you need as extras; each is loaded only when a connection uses it.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
pip install "bauta[postgresql,oracle]"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It isn't on PyPI yet. Until the first release, install from a clone:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
git clone https://github.com/ribeiro11075/bauta.git
|
|
29
|
+
cd bauta
|
|
30
|
+
pip install -e ".[postgresql,oracle]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Extra | Driver | Needs besides pip |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `mysql`, `mariadb` | mysql-connector-python | nothing |
|
|
36
|
+
| `postgresql` | psycopg2, built from source | a C compiler and PostgreSQL's client library (below) |
|
|
37
|
+
| `oracle` | oracledb, in thin mode | nothing — no Oracle client |
|
|
38
|
+
| `mssql` | pymssql | nothing |
|
|
39
|
+
| `sqlite` | Python's own `sqlite3` | nothing |
|
|
40
|
+
| `fpe` | cryptography, for the `fpe` masking strategy | nothing; `oracle` already brings it |
|
|
41
|
+
| `all` | every driver above | as for `postgresql` |
|
|
42
|
+
|
|
43
|
+
**Building psycopg2.** pip compiles it, so it needs a compiler and `pg_config`:
|
|
44
|
+
|
|
45
|
+
- macOS: `xcode-select --install`, then `brew install libpq` and `export PATH="$(brew --prefix libpq)/bin:$PATH"` (Homebrew doesn't put libpq on the path by itself).
|
|
46
|
+
- Debian or Ubuntu: `apt install build-essential libpq-dev`.
|
|
47
|
+
|
|
48
|
+
To skip the build, leave `postgresql` (and `all`) out and install the prebuilt driver beside the other extras: `pip install "bauta[mysql,oracle,mssql]" psycopg2-binary`. psycopg2's maintainers recommend the source build for production.
|
|
49
|
+
|
|
50
|
+
**The native masker (optional).** `bauta-rs`, in `mask-rs/`, masks in Rust: four to five times the throughput, identical masks, nothing to configure. With [Rust](https://rustup.rs) 1.83 or newer, pip builds it in the same command as the rest:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
pip install -e ".[all]" ./mask-rs/py
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Without Rust, leave `./mask-rs/py` off; everything works, only slower. See [the native masker](docs/masking.md#the-native-masker).
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
mkdir configuration
|
|
63
|
+
cp example/starter/configuration/*.yaml configuration/
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Edit `configuration/database.yaml` and `configuration/jobs.yaml` for your databases, then supply the credentials they reference:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
export SOURCE_DB_PASSWORD=... TARGET_DB_PASSWORD=... MASKING_KEY=...
|
|
70
|
+
|
|
71
|
+
bauta validate # check the configuration, offline
|
|
72
|
+
bauta run --dry-run # check connections and tables, moving nothing
|
|
73
|
+
bauta run # run every job once
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To see it work without any of that, using throwaway SQLite databases:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
python example/walkthrough/demo.py # the whole workflow: discover, subset, audit, mask, verify, synthesize
|
|
80
|
+
python example/incremental/demo.py # streaming and incremental loads
|
|
81
|
+
python example/masking/demo.py # masking, discovery and a subset, from Python
|
|
82
|
+
python example/native-masking/demo.py # the same job masked in Python and in Rust, compared
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## The command
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
bauta run run data jobs once, masking any with a `masking` section
|
|
90
|
+
bauta validate check configuration without connecting
|
|
91
|
+
bauta jobs show the job graph and what's due
|
|
92
|
+
bauta history show recent job outcomes recorded with --history
|
|
93
|
+
|
|
94
|
+
bauta discover propose a masking policy for tables
|
|
95
|
+
bauta subset generate jobs that copy a referentially complete subset
|
|
96
|
+
bauta schema create target tables from source ones, in the target's dialect
|
|
97
|
+
bauta synthesize fill tables with generated rows, for data that can't be copied
|
|
98
|
+
bauta clear empty the target tables of jobs, children first
|
|
99
|
+
|
|
100
|
+
bauta audit report what each job does with data, and what to question
|
|
101
|
+
bauta verify-manifest check a manifest is unaltered, and who signed it
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Exit code | Meaning |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `0` | every job completed |
|
|
107
|
+
| `1` | a job failed, or was skipped because a predecessor failed, or the command failed on a database error |
|
|
108
|
+
| `2` | invalid configuration or usage |
|
|
109
|
+
| `130` | interrupted by a signal: running jobs finished, the rest were skipped |
|
|
110
|
+
|
|
111
|
+
`run` makes one pass and exits, so it fits under cron or a Kubernetes CronJob. A second `run` sharing the same run state refuses to start while the first is still going. The useful flags:
|
|
112
|
+
|
|
113
|
+
| Flag | |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `--config DIR` | where the YAML lives; default `./configuration` |
|
|
116
|
+
| `--job NAME` | run only this job — without its predecessors, which it warns about |
|
|
117
|
+
| `--force` | ignore `refresh` windows |
|
|
118
|
+
| `--forever` | stay running; for freshness under a minute |
|
|
119
|
+
| `--log-format json` | structured logs for a collector |
|
|
120
|
+
| `--log FILE` | also log to a file, in addition to stderr (`--quiet` silences stderr) |
|
|
121
|
+
| `--memory FILE` | where run state (last runs, watermarks) is kept, overriding `jobs.yaml`'s [`memory`](docs/configuration.md#file-level) |
|
|
122
|
+
| `--memory-database ALIAS` | keep run state in a database table instead |
|
|
123
|
+
| `--history FILE` | append each job's outcome to a JSON-lines history |
|
|
124
|
+
| `--metrics FILE` | write Prometheus metrics for the textfile collector (`--metrics-push URL` for a Pushgateway) |
|
|
125
|
+
| `--notify-url URL` | post to a webhook when a run doesn't succeed; default `$BAUTA_NOTIFY_URL` |
|
|
126
|
+
| `--accept-key-change` | run upsert jobs whose masking key changed since their last run |
|
|
127
|
+
| `--manifest FILE` | write a sealed JSON record of what was masked, and how; signed if `$BAUTA_MANIFEST_KEY` is set |
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
| | |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| [Configuration](docs/configuration.md) | every field, how credentials are read from the environment, and connection options such as TLS |
|
|
135
|
+
| [Masking](docs/masking.md) | strategies, consistent masks across tables, the key, the manifest, `audit`, `discover`, `subset`, `schema`, `synthesize` and `clear` |
|
|
136
|
+
| [How it works](docs/design.md) | streaming, incremental loads, retries, scheduling, and the masking design |
|
|
137
|
+
| [Operating it](docs/operations.md) | run state, history, metrics and notifications |
|
|
138
|
+
| [Security model](docs/security.md) | what masking protects and what it doesn't, the constructions, keys, and a deployment checklist |
|
|
139
|
+
| [Library](docs/library.md) | embedding it in Python, results, memory backends |
|
|
140
|
+
| [Development](docs/development.md) | running the tests, including against real databases |
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## Layout
|
|
144
|
+
|
|
145
|
+
| | |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| `bauta/` | the package; `runner.py` runs jobs, `masking.py` masks, `databaseDialects.py` holds per-database SQL |
|
|
148
|
+
| `mask-rs/` | the optional native masker, in Rust — see [its README](mask-rs/README.md) |
|
|
149
|
+
| `example/` | runnable demos, each with its `configuration/`, and a starter configuration — see [its README](example/README.md) |
|
|
150
|
+
| `docs/` | the documentation above |
|
|
151
|
+
| `tests/` | the test suite |
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from .configuration import (
|
|
2
|
+
BaseJobConfig,
|
|
3
|
+
Configuration,
|
|
4
|
+
ConfigurationError,
|
|
5
|
+
DatabaseConnectionConfig,
|
|
6
|
+
DatabaseType,
|
|
7
|
+
DataJobConfig,
|
|
8
|
+
DataJobsFile,
|
|
9
|
+
InsertStrategy,
|
|
10
|
+
MaskingConfig,
|
|
11
|
+
expandEnvironmentVariables,
|
|
12
|
+
)
|
|
13
|
+
from .databaseDialects import ColumnCategory, DatabaseDialect, ForeignKey, MariaDBDialect, MSSQLDialect, MySQLDialect, OracleDialect, PostgreSQLDialect, SQLiteDialect
|
|
14
|
+
from .database import Database
|
|
15
|
+
from .dependencyGraph import DependencyGraph, JobOutcome, JobStatus
|
|
16
|
+
from .discovery import TableProposal, proposeTable
|
|
17
|
+
from .log import Log
|
|
18
|
+
from .audit import auditJobs, renderAudit
|
|
19
|
+
from .masking import LOCALES, STRATEGIES, MaskingError, MaskingPlan, Strategy, buildMaskingManifest, keyFingerprint, resolveStrategy, sealManifest, \
|
|
20
|
+
verifyManifest
|
|
21
|
+
from .memory import DATABASE_MEMORY_SCHEMA, DatabaseMemory, FileMemory, MemoryBackend, RunInProgressError, exclusiveRun
|
|
22
|
+
from .reporting import DATABASE_HISTORY_SCHEMA, DatabaseHistory, FileHistory, RunHistory, notify, pushMetrics, writeMetricsFile
|
|
23
|
+
from .runner import RunResult, runDataJobs
|
|
24
|
+
from .subset import SubsetError, SubsetPlan, planSubset
|
|
25
|
+
from .synthesize import SynthesisError, planTable, synthesizeTable
|
|
26
|
+
from .transform import Transform, Transformer, TransformError, TransformResolutionError, resolveTransformer
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
'auditJobs',
|
|
30
|
+
'BaseJobConfig',
|
|
31
|
+
'buildMaskingManifest',
|
|
32
|
+
'ColumnCategory',
|
|
33
|
+
'Configuration',
|
|
34
|
+
'ConfigurationError',
|
|
35
|
+
'DATABASE_HISTORY_SCHEMA',
|
|
36
|
+
'DATABASE_MEMORY_SCHEMA',
|
|
37
|
+
'DatabaseHistory',
|
|
38
|
+
'Database',
|
|
39
|
+
'DatabaseConnectionConfig',
|
|
40
|
+
'DatabaseDialect',
|
|
41
|
+
'DatabaseMemory',
|
|
42
|
+
'DatabaseType',
|
|
43
|
+
'exclusiveRun',
|
|
44
|
+
'expandEnvironmentVariables',
|
|
45
|
+
'DataJobConfig',
|
|
46
|
+
'DataJobsFile',
|
|
47
|
+
'DependencyGraph',
|
|
48
|
+
'FileHistory',
|
|
49
|
+
'FileMemory',
|
|
50
|
+
'ForeignKey',
|
|
51
|
+
'InsertStrategy',
|
|
52
|
+
'JobOutcome',
|
|
53
|
+
'JobStatus',
|
|
54
|
+
'keyFingerprint',
|
|
55
|
+
'LOCALES',
|
|
56
|
+
'Log',
|
|
57
|
+
'MariaDBDialect',
|
|
58
|
+
'MaskingConfig',
|
|
59
|
+
'MaskingError',
|
|
60
|
+
'MaskingPlan',
|
|
61
|
+
'MemoryBackend',
|
|
62
|
+
'MSSQLDialect',
|
|
63
|
+
'notify',
|
|
64
|
+
'MySQLDialect',
|
|
65
|
+
'OracleDialect',
|
|
66
|
+
'planSubset',
|
|
67
|
+
'PostgreSQLDialect',
|
|
68
|
+
'proposeTable',
|
|
69
|
+
'pushMetrics',
|
|
70
|
+
'RunHistory',
|
|
71
|
+
'RunInProgressError',
|
|
72
|
+
'RunResult',
|
|
73
|
+
'SQLiteDialect',
|
|
74
|
+
'STRATEGIES',
|
|
75
|
+
'Strategy',
|
|
76
|
+
'SubsetError',
|
|
77
|
+
'SubsetPlan',
|
|
78
|
+
'SynthesisError',
|
|
79
|
+
'synthesizeTable',
|
|
80
|
+
'planTable',
|
|
81
|
+
'TableProposal',
|
|
82
|
+
'Transform',
|
|
83
|
+
'Transformer',
|
|
84
|
+
'TransformError',
|
|
85
|
+
'TransformResolutionError',
|
|
86
|
+
'renderAudit',
|
|
87
|
+
'resolveStrategy',
|
|
88
|
+
'resolveTransformer',
|
|
89
|
+
'sealManifest',
|
|
90
|
+
'verifyManifest',
|
|
91
|
+
'writeMetricsFile',
|
|
92
|
+
'runDataJobs',
|
|
93
|
+
]
|