mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-24 11:06:24 +02:00
feat(filters): add active_docker_container_count filter and use it for fair resource splits
Compute per-container CPU/RAM shares based on active services (web-/svc-*, enabled=true or undefined). Cast host facts to numbers, add safe min=1, and output compose-ready values. Include robust unit test. Also: include resource.yml.j2 in base template and minor formatting tidy-up. https://chatgpt.com/share/68d2d96c-9bf4-800f-bbec-d4f2c0051c06
This commit is contained in:
79
filter_plugins/active_docker.py
Normal file
79
filter_plugins/active_docker.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Ansible filter to count active docker services for current host.
|
||||
|
||||
Active means:
|
||||
- application key is in group_names
|
||||
- application key matches prefix regex (default: ^(web-|svc-).* )
|
||||
- under applications[app]['docker']['services'] each service is counted if:
|
||||
- 'enabled' is True, OR
|
||||
- 'enabled' is missing/undefined (treated as active)
|
||||
|
||||
Returns an integer. If ensure_min_one=True, returns at least 1.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, Mapping, Iterable
|
||||
|
||||
|
||||
def _is_mapping(x: Any) -> bool:
|
||||
# be liberal: Mapping covers dict-like; fallback to dict check
|
||||
try:
|
||||
return isinstance(x, Mapping)
|
||||
except Exception:
|
||||
return isinstance(x, dict)
|
||||
|
||||
|
||||
def active_docker_container_count(applications: Mapping[str, Any],
|
||||
group_names: Iterable[str],
|
||||
prefix_regex: str = r'^(web-|svc-).*',
|
||||
ensure_min_one: bool = False) -> int:
|
||||
if not _is_mapping(applications):
|
||||
return 1 if ensure_min_one else 0
|
||||
|
||||
group_set = set(group_names or [])
|
||||
try:
|
||||
pattern = re.compile(prefix_regex)
|
||||
except re.error:
|
||||
pattern = re.compile(r'^(web-|svc-).*') # fallback
|
||||
|
||||
count = 0
|
||||
|
||||
for app_key, app_val in applications.items():
|
||||
# host selection + name prefix
|
||||
if app_key not in group_set:
|
||||
continue
|
||||
if not pattern.match(str(app_key)):
|
||||
continue
|
||||
|
||||
docker = app_val.get('docker') if _is_mapping(app_val) else None
|
||||
services = docker.get('services') if _is_mapping(docker) else None
|
||||
if not _is_mapping(services):
|
||||
# sometimes roles define a single service name string; ignore
|
||||
continue
|
||||
|
||||
for _svc_name, svc_cfg in services.items():
|
||||
if not _is_mapping(svc_cfg):
|
||||
# allow shorthand like: service: {} or image string -> counts as enabled
|
||||
count += 1
|
||||
continue
|
||||
enabled = svc_cfg.get('enabled', True)
|
||||
if isinstance(enabled, bool):
|
||||
if enabled:
|
||||
count += 1
|
||||
else:
|
||||
# non-bool enabled -> treat "truthy" as enabled
|
||||
if bool(enabled):
|
||||
count += 1
|
||||
|
||||
if ensure_min_one and count < 1:
|
||||
return 1
|
||||
return count
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
# usage: {{ applications | active_docker_container_count(group_names) }}
|
||||
'active_docker_container_count': active_docker_container_count,
|
||||
}
|
Reference in New Issue
Block a user