import argparse
import json
import logging
import shutil
import subprocess
import sys
DEFAULT_CONTAINER_NAME = "msinsight-streamer"
IMAGE_REPOSITORY_NAME = "msinsight"
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
def get_docker_command():
"""Return the absolute Docker executable path to avoid PATH lookup surprises."""
docker_command = shutil.which("docker")
if not docker_command:
logging.error("Docker executable was not found in PATH.")
sys.exit(1)
return docker_command
def run_command(command, check=True):
"""Run a command and capture stdout/stderr for parsing or error reporting."""
return subprocess.run(command, capture_output=True, text=True, check=check)
def image_matches(image):
"""Return True when a running container uses an msinsight image repository."""
repository = image.split(":", 1)[0].lower().split("/")[-1]
return repository == IMAGE_REPOSITORY_NAME
def get_docker_containers():
"""Return running Docker containers with the fields needed for matching and display."""
try:
result = run_command([get_docker_command(), "ps", "--format", "{{json .}}"])
except (subprocess.CalledProcessError, FileNotFoundError) as exc:
logging.error("failed to list Docker containers: %s", exc)
sys.exit(1)
containers = []
for line in result.stdout.splitlines():
if not line.strip():
continue
data = json.loads(line)
containers.append(
{
"id": data.get("ID", ""),
"name": data.get("Names", ""),
"image": data.get("Image", ""),
"ports": data.get("Ports", ""),
}
)
return containers
def find_matching_containers(container_name=None, port=None, all_insight=False):
"""Find containers by explicit name, exposed host port, or msinsight image repository."""
matches = []
for container in get_docker_containers():
if container_name and container["name"] == container_name:
matches.append(container)
continue
if port and f":{port}->" in container["ports"]:
matches.append(container)
continue
if all_insight and image_matches(container["image"]):
matches.append(container)
continue
if not container_name and not port and container["name"] == DEFAULT_CONTAINER_NAME:
matches.append(container)
return matches
def stop_container(container_name):
"""Stop a container, first renaming it to release the original name quickly."""
removal_name = f"{container_name}-removal"
rename_result = subprocess.run(
[get_docker_command(), "rename", container_name, removal_name],
capture_output=True,
text=True,
check=False,
)
if rename_result.returncode == 0:
target_name = removal_name
else:
logging.warning("failed to rename container '%s' to '%s'.", container_name, removal_name)
if rename_result.stderr:
logging.warning("%s", rename_result.stderr.strip())
target_name = container_name
subprocess.run([get_docker_command(), "stop", target_name], check=True)
logging.info("Container '%s' stopped successfully.", container_name)
def parse_args():
"""Parse stop filters. Without filters, the default container name is used."""
parser = argparse.ArgumentParser(
description="Stop local MindStudio Insight Docker containers by name, port, or msinsight image repository."
)
parser.add_argument(
"-n", "--container-name", default=None, help=f"Container name to stop. Defaults to {DEFAULT_CONTAINER_NAME}."
)
parser.add_argument("-p", "--port", type=str, default=None, help="Stop the container exposing this host port.")
parser.add_argument(
"-a",
"--all",
action="store_true",
help=f"Stop all running containers whose image repository is {IMAGE_REPOSITORY_NAME}.",
)
parser.add_argument("-y", "--yes", action="store_true", help="Automatically confirm stopping containers.")
return parser.parse_args()
def main():
args = parse_args()
matches = find_matching_containers(container_name=args.container_name, port=args.port, all_insight=args.all)
if not matches:
target = args.container_name or (f"port {args.port}" if args.port else DEFAULT_CONTAINER_NAME)
logging.info("No running Insight container found for %s.", target)
return
seen = set()
for container in matches:
name = container["name"]
if name in seen:
continue
seen.add(name)
if not args.yes:
answer = (
input(
f"Container '{name}' with image '{container['image']}' is running on ports '{container['ports']}'. "
"Stop it? (yes/no): "
)
.strip()
.lower()
)
if answer not in {"y", "yes"}:
logging.info("Container '%s' not stopped.", name)
continue
stop_container(name)
if __name__ == "__main__":
main()