Config models¶
All integrations use pydantic models as the primary way to validate and interface with configuration.
As config spec data types are based on OpenAPI 3, we automatically generate the necessary code.
The models reside in a package named config_models
located at the root of a check's namespaced package. For example, a new integration named foo
:
foo
│ ...
├── datadog_checks
│ └── foo
│ └── config_models
│ ├── __init__.py
│ ├── defaults.py
│ ├── instance.py
│ ├── shared.py
│ └── validators.py
│ └── __init__.py
│ ...
...
There are 2 possible models:
SharedConfig
(ID:shared
) that corresponds to theinit_config
sectionInstanceConfig
(ID:instance
) that corresponds to a check's entry in theinstances
section
All models are defined in <ID>.py
and are available for import directly under config_models
.
Default values¶
The default values for optional settings are populated in defaults.py
and are derived from the value property of config spec options.
The precedence is:
- the
default
key - the
example
key, if it appears to represent a real value rather than an illustrative example and thetype
is a primitive - the default value of the
type
e.g.string
->str()
,object
->dict()
, etc.
Validation¶
The validation of fields for every model occurs in 6 stages.
Initial¶
def initialize_<ID>(values: dict[str, Any], **kwargs) -> dict[str, Any]:
...
If such a validator exists in validators.py
, then it is called once with the raw config that was supplied by the user. The returned mapping is used as the input config for the subsequent stages.
Default value population¶
If a field was not supplied by the user nor during the initialization stage, then its default value is taken from defaults.py
. This stage is skipped for required fields.
Default field validators¶
At this point pydantic
will parse the values and perform validation of types, etc.
Custom field validators¶
The contents of validators.py
are entirely custom and contain functions to perform extra validation if necessary.
def <ID>_<OPTION_NAME>(value: Any, *, field: pydantic.fields.ModelField, **kwargs) -> Any:
...
Such validators are called for the appropriate field of the proper model if the option was supplied by the user.
The returned value is used as the new value of the option for the subsequent stages.
Pre-defined field validators¶
A new validators
key under the value property of config spec options is considered. Every entry will refer to a relative import path to a field validator under datadog_checks.base.utils.models.validation
and is executed in the defined order.
The last returned value is used as the new value of the option for the final stage.
Final¶
def finalize_<ID>(values: dict[str, Any], **kwargs) -> dict[str, Any]:
...
If such a validator exists in validators.py
, then it is called with the cumulative result of all fields.
The returned mapping is used to instantiate the model.
Loading¶
A check initialization occurs before a check's first run that loads the config models. Validation errors will thus prevent check execution.
Interface¶
The config models package contains a class ConfigMixin
from which checks inherit:
from datadog_checks.base import AgentCheck
from .config_models import ConfigMixin
class Check(AgentCheck, ConfigMixin):
...
It exposes the instantiated InstanceConfig
model at self.config
and SharedConfig
model at self.shared_config
.
Immutability¶
All generated models are configured as immutable. Additionally, every list
is converted to tuple
and every dict
is converted to immutables.Map.
Deprecation¶
Every option marked as deprecated in the config spec will log a warning with information about when it will be removed and what to do.
Enforcement¶
A validation command ddev validate models
runs in our CI. To locally generate the proper files, run ddev validate models [CHECK] --sync
.