feat: cierra issues 0050 y 0052 + commands automáticos
- 0050: jupyter_exec reescrito sin Y.js (REST + KernelClient). Bug raíz adicional: HEAD /api/contents da 405 → cambiado a GET. 9 tests (5 unit + 4 e2e). - 0052: footprint_aurgi cerrado. Bug fix en setup_geo_stack_docker_pipeline (verify aborta si compose up falla; nombre de contenedor incorrecto). - Nueva primitiva docker_container_running_py_infra (7 tests). - /full-git-push y /full-git-pull pasan a modo automático: auto-commit + push sin preguntar, aborta solo si detecta secrets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: docker_container_running
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "docker_container_running(name: str, timeout: float = 5.0) -> bool"
|
||||
description: "Comprueba si un contenedor Docker existe y está corriendo. True solo si docker inspect retorna State.Running=true. Cualquier fallo (docker ausente, contenedor inexistente, daemon caído, timeout) devuelve False sin excepción."
|
||||
tags: [docker, container, infra, healthcheck]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [subprocess]
|
||||
params:
|
||||
- name: name
|
||||
desc: "Nombre o ID del contenedor a comprobar. Match exacto, no acepta wildcards."
|
||||
- name: timeout
|
||||
desc: "Timeout en segundos para `docker inspect`. Default 5.0. Si se supera devuelve False."
|
||||
output: "True si el contenedor existe y está corriendo. False en cualquier otro caso (no existe, está parado, docker no disponible, timeout)."
|
||||
tested: true
|
||||
tests:
|
||||
- "True para contenedor en ejecución (mock subprocess)"
|
||||
- "False cuando docker inspect retorna 'false'"
|
||||
- "False cuando el contenedor no existe (returncode != 0)"
|
||||
- "False cuando docker no está instalado (FileNotFoundError)"
|
||||
- "False cuando se excede el timeout (TimeoutExpired)"
|
||||
- "Integration: comprueba contenedor real si docker disponible"
|
||||
test_file_path: "python/functions/infra/tests/test_docker_container_running.py"
|
||||
file_path: "python/functions/infra/docker_container_running.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from python.functions.infra.docker_container_running import docker_container_running
|
||||
|
||||
if docker_container_running("better_maps_valhalla"):
|
||||
print("Valhalla up")
|
||||
else:
|
||||
print("Valhalla down")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Wrapper minimalista sobre `docker inspect -f '{{.State.Running}}'`. Pensado como
|
||||
primitiva componible para verificadores de stack y health checks. No lanza
|
||||
excepciones — un fallo de cualquier tipo se reporta como False, dejando que
|
||||
el caller decida qué hacer (skip de test, reintentar, levantar el stack, etc.).
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Comprueba si un contenedor Docker está corriendo por nombre."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
def docker_container_running(name: str, timeout: float = 5.0) -> bool:
|
||||
"""Devuelve True si el contenedor `name` existe y está corriendo.
|
||||
|
||||
Usa `docker inspect -f '{{.State.Running}}' <name>`. Cualquier fallo
|
||||
(docker no instalado, contenedor inexistente, daemon caído, timeout)
|
||||
se interpreta como False.
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
["docker", "inspect", "-f", "{{.State.Running}}", name],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
||||
return False
|
||||
|
||||
if proc.returncode != 0:
|
||||
return False
|
||||
|
||||
return proc.stdout.strip().lower() == "true"
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Tests para docker_container_running."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", ".."))
|
||||
|
||||
from python.functions.infra.docker_container_running import docker_container_running
|
||||
|
||||
|
||||
def _make_completed(stdout: str, returncode: int = 0) -> subprocess.CompletedProcess:
|
||||
return subprocess.CompletedProcess(args=[], returncode=returncode, stdout=stdout, stderr="")
|
||||
|
||||
|
||||
def test_running_true():
|
||||
with patch("subprocess.run", return_value=_make_completed("true\n", 0)):
|
||||
assert docker_container_running("anything") is True
|
||||
|
||||
|
||||
def test_running_false_when_state_false():
|
||||
with patch("subprocess.run", return_value=_make_completed("false\n", 0)):
|
||||
assert docker_container_running("stopped_container") is False
|
||||
|
||||
|
||||
def test_running_false_when_inspect_fails():
|
||||
# docker inspect retorna != 0 cuando el contenedor no existe
|
||||
with patch("subprocess.run", return_value=_make_completed("", 1)):
|
||||
assert docker_container_running("nope") is False
|
||||
|
||||
|
||||
def test_running_false_when_docker_missing():
|
||||
with patch("subprocess.run", side_effect=FileNotFoundError):
|
||||
assert docker_container_running("any") is False
|
||||
|
||||
|
||||
def test_running_false_on_timeout():
|
||||
with patch("subprocess.run", side_effect=subprocess.TimeoutExpired(cmd="docker", timeout=5.0)):
|
||||
assert docker_container_running("any") is False
|
||||
|
||||
|
||||
def test_running_strips_and_lowercases():
|
||||
# docker a veces emite con trailing whitespace; aceptamos "True\n" y "TRUE"
|
||||
for stdout in ("True\n", "TRUE", " true "):
|
||||
with patch("subprocess.run", return_value=_make_completed(stdout, 0)):
|
||||
assert docker_container_running("c") is True
|
||||
|
||||
|
||||
def test_running_integration_real_docker():
|
||||
"""Si docker está disponible, comprueba con un contenedor inexistente y devuelve False."""
|
||||
if not shutil.which("docker"):
|
||||
pytest.skip("docker no disponible en el PATH")
|
||||
assert docker_container_running("__definitely_does_not_exist_xyz__") is False
|
||||
Reference in New Issue
Block a user