config.py constraints and doc
This commit is contained in:
parent
c0edd3bcc5
commit
370097e5c3
3 changed files with 86 additions and 34 deletions
|
@ -1,6 +1,18 @@
|
|||
# system
|
||||
import os
|
||||
|
||||
#############
|
||||
# REGEX PARTS
|
||||
|
||||
# regex part for a number with no leading zeroes
|
||||
_RE_NUMBER: str = r"[0-9]|[1-9][0-9]*"
|
||||
|
||||
# regex for a semantic version string
|
||||
RE_SEMVER = rf"^{_RE_NUMBER}(?:\.{_RE_NUMBER}(?:\.{_RE_NUMBER})?)?$"
|
||||
|
||||
# regex for a lowercase variable name
|
||||
RE_VARNAME = r"^[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9])$"
|
||||
|
||||
#############
|
||||
# ENVIRONMENT
|
||||
|
||||
|
|
|
@ -1,48 +1,67 @@
|
|||
import re
|
||||
from ipaddress import IPv4Network
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
import pydantic
|
||||
|
||||
from ._constants import RE_SEMVER, RE_VARNAME
|
||||
|
||||
|
||||
class _Storage(pydantic.BaseModel):
|
||||
"""a storage subsection"""
|
||||
|
||||
directory: str
|
||||
directory: Path
|
||||
|
||||
|
||||
class _Project(pydantic.BaseModel):
|
||||
"""a project subsection"""
|
||||
|
||||
name: str
|
||||
name: pydantic.constr(
|
||||
regex=RE_VARNAME
|
||||
)
|
||||
enabled: bool = True
|
||||
storage: Optional[_Storage]
|
||||
override_storage: Optional[_Storage]
|
||||
|
||||
@pydantic.root_validator(pre=True)
|
||||
@classmethod
|
||||
def check_grammar(cls, values):
|
||||
if isinstance(values, dict):
|
||||
def unify_project(cls, values):
|
||||
"""parse different project notations"""
|
||||
|
||||
if "name" in values:
|
||||
# default format
|
||||
return values
|
||||
|
||||
elif len(values) == 1:
|
||||
name, enabled = list(values.items())[0]
|
||||
return {"name": name, "enabled": True if enabled is None else enabled}
|
||||
# short format:
|
||||
# - <name>: <enabled>
|
||||
|
||||
elif isinstance(values, str):
|
||||
return {"name": values}
|
||||
name, enabled = list(values.items())[0]
|
||||
return {
|
||||
"name": name,
|
||||
"enabled": True if enabled is None else enabled
|
||||
}
|
||||
|
||||
else:
|
||||
# undefined format
|
||||
raise ValueError
|
||||
|
||||
|
||||
class _Network(pydantic.BaseModel):
|
||||
"""a network subsection"""
|
||||
|
||||
name: str
|
||||
cidr: str
|
||||
name: pydantic.constr(
|
||||
to_lower=True,
|
||||
regex=RE_VARNAME
|
||||
)
|
||||
cidr: IPv4Network
|
||||
|
||||
|
||||
class Config(pydantic.BaseModel):
|
||||
"""represents a kiwi.yml"""
|
||||
|
||||
version: str
|
||||
version: pydantic.constr(
|
||||
regex=RE_SEMVER
|
||||
)
|
||||
shells: Optional[List[str]]
|
||||
environment: Optional[Dict[str, Optional[str]]]
|
||||
|
||||
|
@ -50,30 +69,51 @@ class Config(pydantic.BaseModel):
|
|||
storage: _Storage
|
||||
network: _Network
|
||||
|
||||
@pydantic.validator("version")
|
||||
@classmethod
|
||||
def check_version(cls, value: str) -> str:
|
||||
if not re.match(r"^[0-9]+(\.[0-9]+(\.[0-9]+)?)?$", value):
|
||||
raise ValueError
|
||||
|
||||
return value
|
||||
|
||||
@pydantic.validator("environment", pre=True)
|
||||
@classmethod
|
||||
def unify_env(cls, value) -> Optional[Dict[str, Optional[str]]]:
|
||||
if isinstance(value, dict):
|
||||
def unify_environment(cls, value) -> Optional[Dict[str, Optional[str]]]:
|
||||
"""parse different environment notations"""
|
||||
|
||||
def parse_str(var_val: str) -> (str, Optional[str]):
|
||||
"""parse a "<variable>=<value>" string"""
|
||||
|
||||
idx = var_val.find("=")
|
||||
if idx == -1:
|
||||
# don't split, just define the variable
|
||||
return var_val, None
|
||||
else:
|
||||
# split string, set variable to value
|
||||
return var_val[:idx], var_val[idx + 1:]
|
||||
|
||||
if value is None:
|
||||
# empty environment
|
||||
return None
|
||||
|
||||
elif isinstance(value, dict):
|
||||
# native dict format
|
||||
return value
|
||||
|
||||
elif isinstance(value, list):
|
||||
# list format (multiple strings)
|
||||
|
||||
result: Dict[str, Optional[str]] = {}
|
||||
for item in value:
|
||||
idx = item.find("=")
|
||||
if idx == -1:
|
||||
key, value = item, None
|
||||
else:
|
||||
key, value = item[:idx], item[idx + 1:]
|
||||
|
||||
key, value = parse_str(item)
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
elif isinstance(value, str):
|
||||
# string format (single variable):
|
||||
# "<var>=<value>"
|
||||
|
||||
key, value = parse_str(value)
|
||||
return {key: value}
|
||||
|
||||
elif isinstance(value, int):
|
||||
# integer format (just define single oddly named variable)
|
||||
return {str(value): None}
|
||||
|
||||
else:
|
||||
return None
|
||||
# undefined format
|
||||
raise ValueError
|
||||
|
|
|
@ -4,7 +4,7 @@ shells:
|
|||
projects:
|
||||
- name: admin
|
||||
enabled: true
|
||||
- test:
|
||||
- Test:
|
||||
- test2: false
|
||||
storage:
|
||||
directory: /var/local/kiwi
|
||||
|
|
Loading…
Reference in a new issue