implemented utility function for merging dictionaries
This commit is contained in:
parent
f47d3eb0a8
commit
9a0e34b856
@ -0,0 +1,44 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from fastapi.params import Depends
|
||||||
|
from sqlalchemy import select, insert, delete
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
|
from pillar_tool.db.models.top_data import State, StateAssignment
|
||||||
|
from pillar_tool.schemas import PillarParams, get_model_from_query
|
||||||
|
from pillar_tool.util.validation import validate_state_name
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/pillar",
|
||||||
|
tags=["pillar"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Note: there is no list of all pillars, as this would not be helpful
|
||||||
|
|
||||||
|
@router.get("/{name}")
|
||||||
|
def state_get(req: Request, name: str, params: Depends(get_model_from_query(PillarParams))):
|
||||||
|
# TODO: implement
|
||||||
|
# this function should_
|
||||||
|
# - get the affected host hierarchy
|
||||||
|
# - get all the relevant pillar dictionaries
|
||||||
|
# - merge the pillar directories
|
||||||
|
# - return the merged pillar directory
|
||||||
|
# if any error happens, return non-200 status and an empty dictionary so that salt does not shit itself
|
||||||
|
db: Session = req.state.db
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{name}")
|
||||||
|
def state_create(req: Request, name: str):
|
||||||
|
# TODO: implement
|
||||||
|
|
||||||
|
db = req.state.db
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{name}")
|
||||||
|
def state_delete(req: Request, name: str):
|
||||||
|
# TODO: implement
|
||||||
|
db = req.state.db
|
||||||
@ -50,7 +50,11 @@ class HostgroupParams(BaseModel):
|
|||||||
class StateParams(BaseModel):
|
class StateParams(BaseModel):
|
||||||
pass # No parameters needed for state operations currently
|
pass # No parameters needed for state operations currently
|
||||||
|
|
||||||
|
# Pillar operations
|
||||||
|
class PillarParams(BaseModel):
|
||||||
|
target: str # must be host or hostgroup
|
||||||
|
value: str | None # value if the pillar should be set
|
||||||
|
type: str | None # type of pillar if pillar should be set
|
||||||
|
|
||||||
|
|
||||||
def get_model_from_query[T](model: T) -> Callable[[Request], T]:
|
def get_model_from_query[T](model: T) -> Callable[[Request], T]:
|
||||||
|
|||||||
52
pillar_tool/util/pillar_utilities.py
Normal file
52
pillar_tool/util/pillar_utilities.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
|
def apply_layer(base: dict, layer: dict):
|
||||||
|
"""
|
||||||
|
Recursively applies key-value pairs from `layer` onto `base`.
|
||||||
|
|
||||||
|
For each key in `layer`:
|
||||||
|
- If both `base[key]` and `layer[key]` are dictionaries, recursively merge them.
|
||||||
|
- Otherwise, `base[key]` is overwritten (or newly inserted) with `layer[key]`.
|
||||||
|
|
||||||
|
Note: This function mutates the `base` dictionary in-place.
|
||||||
|
|
||||||
|
:param base: The target dictionary to be updated. Will be modified directly.
|
||||||
|
:param layer: The source dictionary whose values will be applied to `base`.
|
||||||
|
"""
|
||||||
|
for key, value in layer.items():
|
||||||
|
# if base and layer value are dicts, apply recursively
|
||||||
|
if type(value) is dict and key in base and type(base[key]) is dict:
|
||||||
|
apply_layer(base[key], value)
|
||||||
|
# else replace the base value with the layer value
|
||||||
|
# or insert the base value
|
||||||
|
else:
|
||||||
|
base[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def merge(*pillar_data, deep_copy=True) -> dict:
|
||||||
|
"""
|
||||||
|
Merges multiple pillar data dictionaries into one.
|
||||||
|
|
||||||
|
The merging is done left-to-right: keys from later dictionaries override
|
||||||
|
those in earlier ones. Nested dictionaries are merged recursively using
|
||||||
|
`apply_layer`.
|
||||||
|
|
||||||
|
:param pillar_data: Two or more dictionaries to merge. Must contain at least one item.
|
||||||
|
:param deep_copy: If True (default), the first dictionary is deep-copied before merging,
|
||||||
|
preserving the original input data. If False, the first dictionary
|
||||||
|
is modified in-place.
|
||||||
|
:return: A new merged dictionary (if `deep_copy=True`) or the mutated first dictionary (if `deep_copy=False`).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
merge({'a': 1}, {'b': 2}) → {'a': 1, 'b': 2}
|
||||||
|
merge({'a': {'x': 1}}, {'a': {'y': 2}}) → {'a': {'x': 1, 'y': 2}}
|
||||||
|
"""
|
||||||
|
assert len(pillar_data) > 0, "At least one pillar data is required"
|
||||||
|
merged_pillar = deepcopy(pillar_data[0]) if deep_copy else pillar_data[0]
|
||||||
|
|
||||||
|
for pillar in pillar_data[1:]:
|
||||||
|
apply_layer(merged_pillar, pillar)
|
||||||
|
|
||||||
|
|
||||||
|
return merged_pillar
|
||||||
Loading…
x
Reference in New Issue
Block a user