Secrets Backend¶
Spark Expectations uses a pluggy-based plugin system to resolve sensitive values (e.g., Kafka authentication tokens, SMTP passwords) from secure secret stores at runtime. Two backends are included out of the box: Cerberus and Databricks Secrets.
How It Works¶
flowchart TD
Config["User config with secret_type"] --> Check{"secret_type value?"}
Check -->|cerberus| Cerberus["CerberusClient.get_secrets_data"]
Check -->|databricks| DBX["dbutils.secrets.get with scope and key"]
Check -->|custom| CustomPlugin["Custom pluggy plugin"]
Cerberus --> SecretValue["Return secret value"]
DBX --> SecretValue
CustomPlugin --> SecretValue
When streaming stats to Kafka is enabled, Spark Expectations needs credentials to authenticate with the Kafka broker. Rather than passing secrets in plain text, you configure a secret_type and the corresponding backend-specific keys. The framework then resolves the actual secret values at runtime.
Configuration¶
Set the secret_type in your stats_streaming_options dictionary to choose which backend to use.
Cerberus¶
from spark_expectations.config.user_config import Constants as user_config
stats_streaming_config = {
user_config.se_enable_streaming: True,
user_config.secret_type: "cerberus",
user_config.cbs_url: "https://cerberus.example.com",
user_config.cbs_sdb_path: "app/your-sdb-path",
user_config.cbs_kafka_server_url: "kafka_server_url_key",
user_config.cbs_secret_token_url: "auth_token_url_key",
user_config.cbs_secret_app_name: "auth_app_name_key",
user_config.cbs_secret_token: "auth_token_key",
user_config.cbs_topic_name: "topic_name_key",
}
| Constant | Description |
|---|---|
secret_type |
Must be "cerberus" |
cbs_url |
Cerberus server URL |
cbs_sdb_path |
Secure Data Block path in Cerberus |
cbs_kafka_server_url |
Key within the SDB for the Kafka server URL |
cbs_secret_token_url |
Key for the authentication token URL |
cbs_secret_app_name |
Key for the authentication app name |
cbs_secret_token |
Key for the authentication token |
cbs_topic_name |
Key for the Kafka topic name |
Note
The cerberus Python package must be installed separately. It is not a dependency of spark-expectations.
Databricks Secrets¶
from spark_expectations.config.user_config import Constants as user_config
stats_streaming_config = {
user_config.se_enable_streaming: True,
user_config.secret_type: "databricks",
user_config.dbx_workspace_url: "https://workspace.cloud.databricks.com",
user_config.dbx_secret_scope: "your_secret_scope",
user_config.dbx_kafka_server_url: "se_streaming_server_url_secret_key",
user_config.dbx_secret_token_url: "se_streaming_auth_secret_token_url_key",
user_config.dbx_secret_app_name: "se_streaming_auth_secret_appid_key",
user_config.dbx_secret_token: "se_streaming_auth_secret_token_key",
user_config.dbx_topic_name: "se_streaming_topic_name",
}
| Constant | Description |
|---|---|
secret_type |
Must be "databricks" |
dbx_workspace_url |
Databricks workspace URL |
dbx_secret_scope |
Name of the Databricks secret scope |
dbx_kafka_server_url |
Secret key for the Kafka server URL |
dbx_secret_token_url |
Secret key for the authentication token URL |
dbx_secret_app_name |
Secret key for the authentication app name |
dbx_secret_token |
Secret key for the authentication token |
dbx_topic_name |
Secret key for the Kafka topic name |
SMTP Password via Secrets¶
Secrets backends are also used for SMTP passwords when email notifications require authentication. Configure via se_notifications_smtp_creds_dict:
smtp_creds = {
user_config.secret_type: "databricks",
user_config.dbx_workspace_url: "https://workspace.cloud.databricks.com",
user_config.dbx_secret_scope: "your_secret_scope",
user_config.dbx_smtp_password: "smtp_password_secret_key",
}
user_conf = {
user_config.se_notifications_smtp_creds_dict: smtp_creds,
}
Writing a Custom Secrets Plugin¶
The secrets system uses pluggy. You can register your own backend by implementing the SparkExpectationsSecretPluginSpec hook:
import pluggy
from typing import Optional, Dict
SPARK_EXPECTATIONS_SECRETS_BACKEND = "spark_expectations_secrets_backend"
hook_impl = pluggy.HookimplMarker(SPARK_EXPECTATIONS_SECRETS_BACKEND)
class MyCustomSecretPlugin:
@staticmethod
@hook_impl
def get_secret_value(
secret_key_path: str, secret_dict: Dict[str, str]
) -> Optional[str]:
if secret_dict.get("se.streaming.secret.type", "").lower() == "my_vault":
# Fetch from your custom vault
return my_vault_client.get(secret_key_path)
return None
Register it via a setuptools entry point in your package's pyproject.toml:
[project.entry-points."spark_expectations_secrets_backend"]
my_vault = "my_package.secrets:MyCustomSecretPlugin"
The plugin manager loads all registered entry points automatically.