feat: Add describe and volumes commands to Docker Manager for detailed project insights

This commit is contained in:
Peter Wood
2025-12-08 10:54:29 +00:00
parent a478739f55
commit e850221326
2 changed files with 151 additions and 0 deletions

View File

@@ -35,6 +35,7 @@ To make the command available as dm from any directory, run:
import os
import argparse
import subprocess
import json
from pathlib import Path
# Configuration
@@ -113,6 +114,142 @@ def is_project_running(path):
return False
def describe_project(projects, target):
"""Show detailed information about containers in a project."""
if target not in projects:
print(f"Error: Project '{target}' not found.")
return
path = projects[target]
print(f"Describing project: {target}")
cmd = ["docker", "compose", "ps", "--format", "json"]
res = run_command(cmd, path, capture_output=True)
if res and res.returncode == 0:
lines = res.stdout.strip().split('\n')
found_any = False
for line in lines:
if not line:
continue
try:
container = json.loads(line)
found_any = True
name = container.get('Name', 'N/A')
print("-" * 60)
print(f"Service: {container.get('Service', 'N/A')}")
print(f"Container: {name}")
print(f"Image: {container.get('Image', 'N/A')}")
print(f"State: {container.get('State', 'N/A')}")
print(f"Status: {container.get('Status', 'N/A')}")
ports = container.get('Ports', '')
if ports:
print(f"Ports: {ports}")
# Get details via inspect for better parsing (Labels and Mounts)
inspect_cmd = ["docker", "inspect", name, "--format", "{{json .}}"]
inspect_res = run_command(inspect_cmd, path, capture_output=True)
if inspect_res and inspect_res.returncode == 0:
try:
details = json.loads(inspect_res.stdout.strip())
# Description from labels
labels = details.get("Config", {}).get("Labels", {})
description = labels.get("org.opencontainers.image.description")
if description:
print(f"Description: {description}")
# Volumes/Mounts
mounts = details.get("Mounts", [])
if mounts:
print("Volumes:")
print(f" {'Name/Type':<20} | {'Source (Local Path)':<50} | {'Destination'}")
print(f" {'-'*20} | {'-'*50} | {'-'*20}")
for mount in mounts:
m_type = mount.get("Type")
vol_name = mount.get("Name", "")
source = mount.get("Source", "")
dest = mount.get("Destination", "")
# If it's a bind mount, use "Bind" as name, otherwise use volume name
display_name = vol_name if vol_name else f"[{m_type}]"
# Truncate source if too long for cleaner display, or just let it wrap?
# Let's just print it.
print(f" {display_name:<20} | {source:<50} | {dest}")
except json.JSONDecodeError:
pass
except json.JSONDecodeError:
pass
if not found_any:
print("No containers found for this project.")
else:
print("Failed to get container info.")
def list_project_volumes(projects, target):
"""List volumes used by containers in a project."""
if target not in projects:
print(f"Error: Project '{target}' not found.")
return
path = projects[target]
print(f"Volumes for project: {target}")
print("-" * 100)
print(f"{'Service':<20} | {'Type':<10} | {'Name/Source':<40} | {'Destination'}")
print("-" * 100)
cmd = ["docker", "compose", "ps", "--format", "json"]
res = run_command(cmd, path, capture_output=True)
if res and res.returncode == 0:
lines = res.stdout.strip().split('\n')
found_any = False
for line in lines:
if not line:
continue
try:
container = json.loads(line)
name = container.get('Name', 'N/A')
service = container.get('Service', 'N/A')
# Get details via inspect
inspect_cmd = ["docker", "inspect", name, "--format", "{{json .Mounts}}"]
inspect_res = run_command(inspect_cmd, path, capture_output=True)
if inspect_res and inspect_res.returncode == 0:
try:
mounts = json.loads(inspect_res.stdout.strip())
if mounts:
found_any = True
for mount in mounts:
m_type = mount.get("Type", "unknown")
source = mount.get("Source", "")
dest = mount.get("Destination", "")
name_or_source = mount.get("Name", "")
if m_type == "bind":
name_or_source = source
elif not name_or_source:
name_or_source = source
print(f"{service:<20} | {m_type:<10} | {name_or_source:<40} | {dest}")
except json.JSONDecodeError:
pass
except json.JSONDecodeError:
pass
if not found_any:
print("No volumes found for this project.")
else:
print("Failed to get container info.")
def manage_project(projects, action, target, extra_args=None):
"""
Execute the specified action (stop, update, restart, logs) on target project(s).
@@ -195,6 +332,14 @@ def main():
# List command configuration
subparsers.add_parser("list", help="List running containers")
# Describe command configuration
describe_parser = subparsers.add_parser("describe", help="Show details of a project's containers")
describe_parser.add_argument("project", help="Project name to describe")
# Volumes command configuration
volumes_parser = subparsers.add_parser("volumes", help="List volumes used by a project")
volumes_parser.add_argument("project", help="Project name to list volumes for")
# Stop command configuration
stop_parser = subparsers.add_parser("stop", help="Stop containers")
stop_parser.add_argument("project", nargs="?", default=None,
@@ -237,6 +382,10 @@ def main():
# Dispatch commands
if args.command == "list":
list_containers(projects)
elif args.command == "describe":
describe_project(projects, args.project)
elif args.command == "volumes":
list_project_volumes(projects, args.project)
elif args.command == "logs":
# Prepare extra args for logs
extra_args = []