Skip to content

Commit

Permalink
Add runtime configuration validation (#8968)
Browse files Browse the repository at this point in the history
* Sync config models

* re-sync

* address
  • Loading branch information
ofek authored Nov 8, 2021
1 parent 858bf53 commit b7b251f
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 3 deletions.
1 change: 0 additions & 1 deletion pgbouncer/assets/configuration/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ files:
If the password contains special characters, be sure to escape them using percent encoding.
See: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
required: true
value:
example: postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DATABASE_URL>?sslmode=require
type: string
Expand Down
18 changes: 18 additions & 0 deletions pgbouncer/datadog_checks/pgbouncer/config_models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from .instance import InstanceConfig
from .shared import SharedConfig


class ConfigMixin:
_config_model_instance: InstanceConfig
_config_model_shared: SharedConfig

@property
def config(self) -> InstanceConfig:
return self._config_model_instance

@property
def shared_config(self) -> SharedConfig:
return self._config_model_shared
52 changes: 52 additions & 0 deletions pgbouncer/datadog_checks/pgbouncer/config_models/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from datadog_checks.base.utils.models.fields import get_default_field_value


def shared_service(field, value):
return get_default_field_value(field, value)


def instance_database_url(field, value):
return 'postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DATABASE_URL>?sslmode=require'


def instance_disable_generic_tags(field, value):
return False


def instance_empty_default_hostname(field, value):
return False


def instance_host(field, value):
return get_default_field_value(field, value)


def instance_min_collection_interval(field, value):
return 15


def instance_password(field, value):
return get_default_field_value(field, value)


def instance_port(field, value):
return get_default_field_value(field, value)


def instance_service(field, value):
return get_default_field_value(field, value)


def instance_tags(field, value):
return get_default_field_value(field, value)


def instance_use_cached(field, value):
return True


def instance_username(field, value):
return get_default_field_value(field, value)
52 changes: 52 additions & 0 deletions pgbouncer/datadog_checks/pgbouncer/config_models/instance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

from typing import Optional, Sequence

from pydantic import BaseModel, root_validator, validator

from datadog_checks.base.utils.functions import identity
from datadog_checks.base.utils.models import validation

from . import defaults, validators


class InstanceConfig(BaseModel):
class Config:
allow_mutation = False

database_url: Optional[str]
disable_generic_tags: Optional[bool]
empty_default_hostname: Optional[bool]
host: Optional[str]
min_collection_interval: Optional[float]
password: Optional[str]
port: Optional[int]
service: Optional[str]
tags: Optional[Sequence[str]]
use_cached: Optional[bool]
username: Optional[str]

@root_validator(pre=True)
def _initial_validation(cls, values):
return validation.core.initialize_config(getattr(validators, 'initialize_instance', identity)(values))

@validator('*', pre=True, always=True)
def _ensure_defaults(cls, v, field):
if v is not None or field.required:
return v

return getattr(defaults, f'instance_{field.name}')(field, v)

@validator('*')
def _run_validations(cls, v, field):
if not v:
return v

return getattr(validators, f'instance_{field.name}', identity)(v, field=field)

@root_validator(pre=False)
def _final_validation(cls, values):
return validation.core.finalize_config(getattr(validators, 'finalize_instance', identity)(values))
42 changes: 42 additions & 0 deletions pgbouncer/datadog_checks/pgbouncer/config_models/shared.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

from typing import Optional

from pydantic import BaseModel, root_validator, validator

from datadog_checks.base.utils.functions import identity
from datadog_checks.base.utils.models import validation

from . import defaults, validators


class SharedConfig(BaseModel):
class Config:
allow_mutation = False

service: Optional[str]

@root_validator(pre=True)
def _initial_validation(cls, values):
return validation.core.initialize_config(getattr(validators, 'initialize_shared', identity)(values))

@validator('*', pre=True, always=True)
def _ensure_defaults(cls, v, field):
if v is not None or field.required:
return v

return getattr(defaults, f'shared_{field.name}')(field, v)

@validator('*')
def _run_validations(cls, v, field):
if not v:
return v

return getattr(validators, f'shared_{field.name}', identity)(v, field=field)

@root_validator(pre=False)
def _final_validation(cls, values):
return validation.core.finalize_config(getattr(validators, 'finalize_shared', identity)(values))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# (C) Datadog, Inc. 2021-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
5 changes: 3 additions & 2 deletions pgbouncer/datadog_checks/pgbouncer/data/conf.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ init_config:
#
instances:

## @param database_url - string - required
-
## @param database_url - string - optional - default: postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DATABASE_URL>?sslmode=require
## The PgBouncer stats database URL.
##
## If the password contains special characters, be sure to escape them using percent encoding.
##
## See: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
#
- database_url: postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DATABASE_URL>?sslmode=require
# database_url: postgresql://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DATABASE_URL>?sslmode=require

## @param host - string - optional
## If `database_url` is not used, set up the host to connect to with the `host` parameter.
Expand Down

0 comments on commit b7b251f

Please sign in to comment.