Configurer un service avec Vault et Pydantic

image







Préface



Dans cet article, je parlerai de la configuration de vos services en utilisant le bundle Vault (KV et jusqu'à présent uniquement la première version, c'est-à-dire sans secrets de version) et Pydantic (Paramètres) sous le patronage de Sitri .







Donc, disons que nous avons une application superapp avec des configurations configurées dans Vault et une authentification utilisant approle, configurons quelque chose comme ceci (je laisserai le paramètre de stratégie pour l'accès aux moteurs secrets et aux secrets eux-mêmes dans les coulisses, car c'est assez simple et l'article n'est pas à propos de ça):







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