utils_devops 0.1.140__py3-none-any.whl → 0.1.142__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- utils_devops/extras/docker_ops.py +77 -30
- {utils_devops-0.1.140.dist-info → utils_devops-0.1.142.dist-info}/METADATA +1 -1
- {utils_devops-0.1.140.dist-info → utils_devops-0.1.142.dist-info}/RECORD +5 -5
- {utils_devops-0.1.140.dist-info → utils_devops-0.1.142.dist-info}/WHEEL +0 -0
- {utils_devops-0.1.140.dist-info → utils_devops-0.1.142.dist-info}/entry_points.txt +0 -0
|
@@ -545,7 +545,7 @@ def validate_compose_file(compose_file: str, env_file: Optional[str] = None) ->
|
|
|
545
545
|
# Compose operations
|
|
546
546
|
def compose_up(compose_file: str, services: Optional[List[str]] = None,
|
|
547
547
|
build: bool = False, pull: bool = False, detach: bool = True,
|
|
548
|
-
project_name: Optional[str] = None , env_file: Optional[str] = None) -> bool:
|
|
548
|
+
project_name: Optional[str] = None , env_file: Optional[str] = None , no_build: Optional[bool] = False ,no_pull: Optional[bool] = False) -> bool:
|
|
549
549
|
"""Start compose services with detailed progress logging"""
|
|
550
550
|
cmd = ["up"]
|
|
551
551
|
if detach:
|
|
@@ -556,6 +556,12 @@ def compose_up(compose_file: str, services: Optional[List[str]] = None,
|
|
|
556
556
|
if pull:
|
|
557
557
|
cmd.append("--pull")
|
|
558
558
|
logger.info("⬇️ Pulling latest images before starting...")
|
|
559
|
+
if no_build:
|
|
560
|
+
cmd.append("--no-build")
|
|
561
|
+
logger.info("🔨 Forbidden Building images before starting...")
|
|
562
|
+
if no_pull:
|
|
563
|
+
cmd.append("--no-pull")
|
|
564
|
+
logger.info("⬇️ Forbidden Pulling latest images before starting...")
|
|
559
565
|
if services:
|
|
560
566
|
cmd.extend(services)
|
|
561
567
|
logger.info(f"🚀 Starting services: {services}")
|
|
@@ -767,9 +773,6 @@ def check_service_health(compose_file: str, service: str,
|
|
|
767
773
|
logger.error(f"Error checking health for {service}: {e}")
|
|
768
774
|
return {"healthy": False, "error": str(e)}
|
|
769
775
|
|
|
770
|
-
import time
|
|
771
|
-
from collections import defaultdict
|
|
772
|
-
|
|
773
776
|
def wait_for_healthy(
|
|
774
777
|
compose_file: str,
|
|
775
778
|
services: List[str],
|
|
@@ -797,8 +800,17 @@ def wait_for_healthy(
|
|
|
797
800
|
if overall == 'healthy':
|
|
798
801
|
status_groups[overall].append(service)
|
|
799
802
|
else:
|
|
800
|
-
|
|
801
|
-
|
|
803
|
+
# Customize message based on overall
|
|
804
|
+
msg = f"{service}: {status['status']} (health: {status['health']})"
|
|
805
|
+
if overall == 'starting':
|
|
806
|
+
msg += " | Starting..."
|
|
807
|
+
elif overall == 'restarting':
|
|
808
|
+
msg += " | Restarting..."
|
|
809
|
+
elif overall == 'unhealthy':
|
|
810
|
+
msg += f" | Error: {status['error']}"
|
|
811
|
+
elif overall == 'not found':
|
|
812
|
+
msg += f" | Not found: {status['error']}"
|
|
813
|
+
status_groups[overall].append(msg)
|
|
802
814
|
|
|
803
815
|
# Define order and emojis
|
|
804
816
|
statuses = ['healthy', 'starting', 'restarting', 'unhealthy', 'not found']
|
|
@@ -816,8 +828,10 @@ def wait_for_healthy(
|
|
|
816
828
|
for detail in sorted(group):
|
|
817
829
|
logger.info(f" - {detail}")
|
|
818
830
|
|
|
819
|
-
# Check if all healthy
|
|
820
|
-
|
|
831
|
+
# Check if all healthy using the 'healthy' bool
|
|
832
|
+
all_healthy = all(status.get('healthy', False) for status in detailed_health.values())
|
|
833
|
+
|
|
834
|
+
if all_healthy:
|
|
821
835
|
logger.info(f"🎉 ALL SERVICES HEALTHY! Completed in {elapsed:.1f}s after {attempt} attempts")
|
|
822
836
|
return True
|
|
823
837
|
|
|
@@ -847,17 +861,35 @@ def _compute_overall_status(service_status: Dict[str, Any]) -> str:
|
|
|
847
861
|
if not service_status:
|
|
848
862
|
return "not found"
|
|
849
863
|
|
|
850
|
-
|
|
864
|
+
# Use 'state' for base status (e.g., "running", "restarting")
|
|
865
|
+
state = service_status.get('state', '').lower()
|
|
851
866
|
health = service_status.get('health', 'none').lower()
|
|
867
|
+
status = service_status.get('status', '').lower() # Fallback for parsing
|
|
868
|
+
|
|
869
|
+
# Fallback parsing if state is empty
|
|
870
|
+
if not state:
|
|
871
|
+
if ' (health: ' in status:
|
|
872
|
+
health = status.split('(health: ')[1].split(')')[0].lower()
|
|
873
|
+
elif '(healthy)' in status:
|
|
874
|
+
health = 'healthy'
|
|
875
|
+
base_status = status.split(' (')[0]
|
|
876
|
+
if base_status.startswith('up'):
|
|
877
|
+
state = 'running'
|
|
878
|
+
elif 'restarting' in base_status:
|
|
879
|
+
state = 'restarting'
|
|
880
|
+
elif 'created' in base_status or 'paused' in base_status:
|
|
881
|
+
state = base_status
|
|
882
|
+
else:
|
|
883
|
+
state = 'unknown'
|
|
852
884
|
|
|
853
|
-
if
|
|
885
|
+
if state == 'restarting':
|
|
854
886
|
return "restarting"
|
|
855
887
|
|
|
856
|
-
if
|
|
888
|
+
if state in ['created', 'paused']:
|
|
857
889
|
return "starting" if health == 'starting' else "unhealthy"
|
|
858
890
|
|
|
859
|
-
if
|
|
860
|
-
return "unhealthy"
|
|
891
|
+
if state != 'running':
|
|
892
|
+
return "unhealthy"
|
|
861
893
|
|
|
862
894
|
if health == 'starting':
|
|
863
895
|
return "starting"
|
|
@@ -878,11 +910,11 @@ def check_health_from_compose_ps(compose_file: str) -> Dict[str, Any]:
|
|
|
878
910
|
result = run_compose_command(compose_file, ["ps", "--format", "json"])
|
|
879
911
|
if result.rc != 0:
|
|
880
912
|
return {"healthy": False, "error": f"docker compose ps failed: {result.stderr}"}
|
|
881
|
-
|
|
913
|
+
|
|
882
914
|
# Parse JSON output - handle multiple JSON objects (one per line)
|
|
883
915
|
import json
|
|
884
916
|
containers_data = []
|
|
885
|
-
|
|
917
|
+
|
|
886
918
|
# Split by lines and parse each JSON object separately
|
|
887
919
|
for line in result.stdout.strip().split('\n'):
|
|
888
920
|
if line.strip(): # Skip empty lines
|
|
@@ -892,18 +924,19 @@ def check_health_from_compose_ps(compose_file: str) -> Dict[str, Any]:
|
|
|
892
924
|
except json.JSONDecodeError as e:
|
|
893
925
|
logger.error(f"Failed to parse JSON line: {line}, error: {e}")
|
|
894
926
|
continue
|
|
895
|
-
|
|
927
|
+
|
|
896
928
|
if not containers_data:
|
|
897
929
|
return {"healthy": False, "error": "No container data parsed from output"}
|
|
898
|
-
|
|
930
|
+
|
|
899
931
|
health_status = {}
|
|
900
932
|
all_healthy = True
|
|
901
|
-
|
|
933
|
+
|
|
902
934
|
for container in containers_data:
|
|
903
935
|
service = container.get('Service', '')
|
|
904
936
|
health = container.get('Health', '')
|
|
905
937
|
status = container.get('Status', '')
|
|
906
|
-
|
|
938
|
+
state = container.get('State', '') # Added: the base state like "running"
|
|
939
|
+
|
|
907
940
|
# Determine health status
|
|
908
941
|
is_healthy = False
|
|
909
942
|
if health:
|
|
@@ -911,22 +944,23 @@ def check_health_from_compose_ps(compose_file: str) -> Dict[str, Any]:
|
|
|
911
944
|
else:
|
|
912
945
|
# Fallback to status parsing
|
|
913
946
|
is_healthy = '(healthy)' in status.lower()
|
|
914
|
-
|
|
947
|
+
|
|
915
948
|
health_status[service] = {
|
|
916
949
|
'healthy': is_healthy,
|
|
917
950
|
'health': health,
|
|
918
|
-
'status': status
|
|
951
|
+
'status': status,
|
|
952
|
+
'state': state # Added
|
|
919
953
|
}
|
|
920
|
-
|
|
954
|
+
|
|
921
955
|
if not is_healthy:
|
|
922
956
|
all_healthy = False
|
|
923
|
-
|
|
957
|
+
|
|
924
958
|
return {
|
|
925
959
|
"healthy": all_healthy,
|
|
926
960
|
"services": health_status,
|
|
927
961
|
"details": f"{sum(1 for s in health_status.values() if s['healthy'])}/{len(health_status)} services healthy"
|
|
928
962
|
}
|
|
929
|
-
|
|
963
|
+
|
|
930
964
|
except Exception as e:
|
|
931
965
|
return {"healthy": False, "error": f"Health check failed: {e}"}
|
|
932
966
|
|
|
@@ -1350,24 +1384,34 @@ def _get_detailed_health_status(
|
|
|
1350
1384
|
) -> Dict[str, Dict[str, Any]]:
|
|
1351
1385
|
"""Get detailed health status for each service with overall_status."""
|
|
1352
1386
|
detailed_status = {}
|
|
1353
|
-
|
|
1387
|
+
|
|
1354
1388
|
try:
|
|
1355
1389
|
# Use the reliable JSON parsing method
|
|
1356
1390
|
health_result = check_health_from_compose_ps(compose_file)
|
|
1357
1391
|
all_services_status = health_result.get('services', {})
|
|
1358
|
-
|
|
1392
|
+
|
|
1359
1393
|
for service in services:
|
|
1360
1394
|
service_status = all_services_status.get(service, {})
|
|
1361
1395
|
overall_status = _compute_overall_status(service_status)
|
|
1396
|
+
error_msg = "No error details"
|
|
1397
|
+
if overall_status == "starting":
|
|
1398
|
+
error_msg = "Service is starting"
|
|
1399
|
+
elif overall_status == "restarting":
|
|
1400
|
+
error_msg = "Service is restarting"
|
|
1401
|
+
elif overall_status == "unhealthy":
|
|
1402
|
+
error_msg = f"Service is unhealthy: {service_status.get('status', 'unknown')}"
|
|
1403
|
+
elif overall_status == "not found":
|
|
1404
|
+
error_msg = "Service not found"
|
|
1362
1405
|
detailed_status[service] = {
|
|
1363
1406
|
'healthy': overall_status == 'healthy',
|
|
1364
1407
|
'health': service_status.get('health', 'unknown'),
|
|
1365
1408
|
'status': service_status.get('status', 'not found'),
|
|
1366
|
-
'
|
|
1409
|
+
'state': service_status.get('state', 'unknown'), # Added
|
|
1410
|
+
'error': error_msg,
|
|
1367
1411
|
'overall_status': overall_status
|
|
1368
1412
|
}
|
|
1369
|
-
# Removed
|
|
1370
|
-
|
|
1413
|
+
# Removed individual logging; handled in groups
|
|
1414
|
+
|
|
1371
1415
|
except Exception as e:
|
|
1372
1416
|
logger.error(f"Failed to get detailed health status: {e}")
|
|
1373
1417
|
# Fallback: mark all as unhealthy
|
|
@@ -1376,10 +1420,11 @@ def _get_detailed_health_status(
|
|
|
1376
1420
|
'healthy': False,
|
|
1377
1421
|
'health': 'unknown',
|
|
1378
1422
|
'status': 'check failed',
|
|
1423
|
+
'state': 'unknown',
|
|
1379
1424
|
'error': str(e),
|
|
1380
1425
|
'overall_status': 'unhealthy'
|
|
1381
1426
|
}
|
|
1382
|
-
|
|
1427
|
+
|
|
1383
1428
|
return detailed_status
|
|
1384
1429
|
|
|
1385
1430
|
|
|
@@ -2332,6 +2377,8 @@ def test_compose(
|
|
|
2332
2377
|
compose_file,
|
|
2333
2378
|
services=services,
|
|
2334
2379
|
project_name=project_name,
|
|
2380
|
+
no_build=no_build,
|
|
2381
|
+
no_pull=no_pull,
|
|
2335
2382
|
)
|
|
2336
2383
|
# Assuming compose_up returns a dict with 'success'; adjust if needed
|
|
2337
2384
|
if up_result is not True: # If it doesn't raise, check result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: utils_devops
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.142
|
|
4
4
|
Summary: Lightweight DevOps utilities for automation scripts: config editing (YAML/JSON/INI/.env), templating, diffing, and CLI tools
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: devops,automation,nginx,cli,jinja2,yaml,config,diff,templating,logging,docker,compose,file-ops
|
|
@@ -9,7 +9,7 @@ utils_devops/core/strings.py,sha256=8s0GSjcyTKwLjJjsJ_XfOJxPtyb549icDlU9SUxSvHI,
|
|
|
9
9
|
utils_devops/core/systems.py,sha256=wNbEFUAvbMPdqWN-iXvTzvj5iE9xaWfjZYYvD0EZAH0,47577
|
|
10
10
|
utils_devops/extras/__init__.py,sha256=ZXHeVLHO3_qiW9AY-UQ_YA9cQzmkLGv54a2UbyvtlM0,3571
|
|
11
11
|
utils_devops/extras/aws_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
utils_devops/extras/docker_ops.py,sha256=
|
|
12
|
+
utils_devops/extras/docker_ops.py,sha256=24aNloPc9p7EymA-p974hF4Q6zlgkkrj37ZJXQlEG-U,180031
|
|
13
13
|
utils_devops/extras/git_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
utils_devops/extras/interaction_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
utils_devops/extras/metrics_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -19,7 +19,7 @@ utils_devops/extras/notification_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
|
19
19
|
utils_devops/extras/performance_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
utils_devops/extras/ssh_ops.py,sha256=8I_AF0q76CJOK2qp68w1oro2SVOZ_v7b8OvgDYcE4tg,73741
|
|
21
21
|
utils_devops/extras/vault_ops.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
utils_devops-0.1.
|
|
23
|
-
utils_devops-0.1.
|
|
24
|
-
utils_devops-0.1.
|
|
25
|
-
utils_devops-0.1.
|
|
22
|
+
utils_devops-0.1.142.dist-info/METADATA,sha256=U50DDiYSln6nwVdVl2eT4vSecQgdb1fa0mWbr1eQGj0,1903
|
|
23
|
+
utils_devops-0.1.142.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
24
|
+
utils_devops-0.1.142.dist-info/entry_points.txt,sha256=ei3B6ZL5yu6dOq-U1r8wsBdkXeg63RAyV7m8_ADaE6k,53
|
|
25
|
+
utils_devops-0.1.142.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|