Configurar un servicio con Vault y Pydantic

imagen







Prefacio



En este artículo hablaré sobre la configuración de sus servicios utilizando el paquete Vault (KV y hasta ahora solo la primera versión, es decir, sin secretos de versiones) y Pydantic (Configuración) bajo el patrocinio de Sitri .







Entonces, digamos que tenemos una aplicación superapp con configuraciones configuradas en Vault y autenticación usando Approle, configuremos algo como esto (dejaré la configuración de políticas para acceder a los motores secretos y los secretos en sí mismos detrás de escena, ya que esto es bastante simple y el artículo no es sobre eso):







Key                        Value
---                        -----
bind_secret_id             true
local_secret_ids           false
policies                   [superapp_service]
secret_id_bound_cidrs      <nil>
secret_id_num_uses         0
secret_id_ttl              0s
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              30m
token_no_default_policy    false
token_num_uses             50
token_period               0s
token_policies             [superapp_service]
token_ttl                  20m
token_type                 default
      
      





.: , , secret_id_ttl , 0 .







SuperApp : , kafka faust .







Sitri



, vault-, , , .







, vault- provider_config.py:







import hvac  

from sitri.providers.contrib.vault import VaultKVConfigProvider  
from sitri.providers.contrib.system import SystemConfigProvider  

configurator = SystemConfigProvider(prefix="superapp")  
ENV = configurator.get("env")  

def vault_client_factory() -> hvac.Client:  
    client = hvac.Client(url=configurator.get("vault_api"))  

    client.auth_approle(  
        role_id=configurator.get("role_id"),  
  secret_id=configurator.get("secret_id"),  
  )  

    return client  

provider = VaultKVConfigProvider(  
    vault_connector=vault_client_factory, mount_point=f"{configurator.get('app_name')}/{ENV}"  
)
      
      





vault, .. :







export SUPERAPP_ENV=dev
export SUPERAPP_APP_NAME=superapp
export SUPERAPP_VAULT_API=https://your-vault-host.domain
export SUPERAPP_ROLE_ID=535b268d-b858-5fb9-1e3e-79068ca77e27 # 
export SUPERAPP_SECRET_ID=243ab423-12a2-63dc-3d5d-0b95b1745ccf # 
      
      





, mount_point , SUPERAPP_ENV. settings- , secret_path .









(, Kafka, Faust) .









from pydantic import Field  

from sitri.settings.contrib.vault import VaultKVSettings  

from superapp.config.provider_config import provider  

class DBSettings(VaultKVSettings):  
    user: str = Field(..., vault_secret_key="username")  
    password: str = Field(...)  
    host: str = Field(...)  
    port: int = Field(...)  

    class Config:  
        provider = provider  
        default_secret_path = "db"
      
      





, , . . - superapp/dev/db, , config , pydantic , extra- vault_secret_key — , pydantic , , .







, , , superapp/dev/db, password username, , user .







:







{
  "host": "testhost",
  "password": "testpassword",
  "port": "1234",
  "username": "testuser"
}
      
      





, , , :







db_settings = DBSettings()
pprint(db_settings.dict())
# -> 
# {
#     "host": "testhost",
#     "password": "testpassword",
#     "port": 1234,
#     "user": "testuser"
# }
      
      





Kafka



from typing import Dict, Any  

from pydantic import Field  

from sitri.settings.contrib.vault import VaultKVSettings  

from superapp.config.provider_config import provider, configurator  

class KafkaSettings(VaultKVSettings):  
    mechanism: str = Field(..., vault_secret_key="auth_mechanism")  
    brokers: str = Field(...)  
    auth_data: Dict[str, Any] = Field(...)  

    class Config:  
        provider = provider  
        default_secret_path = "kafka"  
        default_mount_point = f"{configurator.get('app_name')}/common"
      
      





, , , superapp/common/kafka







{
  "auth_data": "{\"password\": \"testpassword\", \"username\": \"testuser\"}",
  "auth_mechanism": "SASL_PLAINTEXT",
  "brokers": "kafka://test"
}
      
      





Dict[str, Any] , :







{
    "auth_data":
    {
        "password": "testpassword",
        "username": "testuser"
    },
    "brokers": "kafka://test",
    "mechanism": "SASL_PLAINTEXT"
}
      
      





, json, :







{
  "auth_data": {
    "password": "testpassword",
    "username": "testuser"
  },
  "auth_mechanism": "SASL_PLAINTEXT",
  "brokers": "kafka://test"
}
      
      





.







P.S.

, secret_path mount_point , ( ). :







Secret path prioritization:

  1. vault_secret_path (Field arg)
  2. default_secret_path (Config class field)
  3. secret_path (provider initialization optional arg)




Mount point prioritization:

  1. vault_mount_point (Field arg)
  2. default_mount_point (Config class field)
  3. mount_point (provider initialization optional arg)




Faust



from typing import Dict  

from pydantic import Field, BaseModel  

from sitri.settings.contrib.vault import VaultKVSettings  

from superapp.config.provider_config import provider  

class AgentConfig(BaseModel):  
    partitions: int = Field(...)  
    concurrency: int = Field(...)  

class FaustSettings(VaultKVSettings):  
    app_name: str = Field(...)  
    default_partitions_count: int = Field(..., vault_secret_key="partitions_count")  
    default_concurrency: int = Field(..., vault_secret_key="agent_concurrency")  
    agents: Dict[str, AgentConfig] = Field(default=None, vault_secret_key="agents_specification")  

    class Config:  
        provider = provider  
        default_secret_path = "faust"
      
      





superapp/dev/faust:







{
  "agent_concurrency": "5",
  "app_name": "superapp-workers",
  "partitions_count": "10"
}
      
      





, - - concurrency . , - :







{
  "agents": None,
  "app_name": "superapp-workers",
  "default_concurrency": 5,
  "default_partitions_count": 10
}
      
      





, X :







{
  "partitions": 5,
  "concurrency": 2
}
      
      





:







{
  "agent_concurrency": "5",
  "agents_specification": {
    "X": {
      "concurrency": "2",
      "partitions": "5"
    }
  },
  "app_name": "superapp-workers",
  "partitions_count": "10"
}
      
      





, AgentConfig:







{
    "agents":
    {
        "X":
        {
            "concurrency": 2,
            "partitions": 5
        }
    },
    "app_name": "superapp-workers",
    "default_concurrency": 5,
    "default_partitions_count": 10
}
      
      







from pydantic import BaseModel, Field  

from superapp.config.database_settings import DBSettings  
from superapp.config.faust_settings import FaustSettings  
from superapp.config.kafka_settings import KafkaSettings  

class AppSettings(BaseModel):  
    db: DBSettings = Field(default_factory=DBSettings)  
    faust: FaustSettings = Field(default_factory=FaustSettings)  
    kafka: KafkaSettings = Field(default_factory=KafkaSettings)
      
      





, default_factory .







, :







from superapp.config import AppSettings  

config = AppSettings()  

print(config)  
print(config.dict())
      
      





:







db=DBSettings(user='testuser', password='testpassword', host='testhost', port=1234) 
faust=FaustSettings(app_name='superapp-workers', default_partitions_count=10, default_concurrency=5, agents={'X': AgentConfig(partitions=5, concurrency=2)}) 
kafka=KafkaSettings(mechanism='SASL_PLAINTEXT', brokers='kafka://test', auth_data={'password': 'testpassword', 'username': 'testuser'})
      
      





{
    "db":
    {
        "host": "testhost",
        "password": "testpassword",
        "port": 1234,
        "user": "testuser"
    },
    "faust":
    {
        "agents":
        {
            "X":
            {
                "concurrency": 2,
                "partitions": 5
            }
        },
        "app_name": "superapp-workers",
        "default_concurrency": 5,
        "default_partitions_count": 10
    },
    "kafka":
    {
        "auth_data":
        {
            "password": "testpassword",
            "username": "testuser"
        },
        "brokers": "kafka://test",
        "mechanism": "SASL_PLAINTEXT"
    }
}
      
      





, , !







-:







superapp
├── config
│   ├── app_settings.py
│   ├── database_settings.py
│   ├── faust_settings.py
│   ├── __init__.py
│   ├── kafka_settings.py
│   └── provider_config.py
├── __init__.py
└── main.py
      
      







Sitri, , vault - .







, . !







P.S. github








All Articles