diff --git a/docker-manager/main.py b/docker-manager/main.py index 9b7da7b..8dcae2c 100644 --- a/docker-manager/main.py +++ b/docker-manager/main.py @@ -1,7 +1,6 @@ -import subprocess import tkinter as tk from tkinter import ttk -from manageDocker import delete_docker_image, get_docker_images +from manageDocker import delete_docker_image, get_docker_images, is_image_in_use def show_toast(instruction): toast = tk.Toplevel() @@ -16,12 +15,6 @@ def delete_item(item_id): def on_delete_button_click(item_id): delete_item(item_id) -def is_image_in_use(image_id): - try: - subprocess.run(f"docker inspect {image_id}", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return True # Image is in use - except subprocess.CalledProcessError as e: - return False # Image is not in use def delete_selected_item(): selected_items = my_tree.selection() diff --git a/docker-manager/manageDocker.py b/docker-manager/manageDocker.py index cda7f89..3ec3e80 100644 --- a/docker-manager/manageDocker.py +++ b/docker-manager/manageDocker.py @@ -47,10 +47,22 @@ def check_images_usage(image_list): def is_image_in_use(image_id): try: - subprocess.run(f"docker inspect {image_id}", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return True # Image exists and can be inspected, so it's in use + result = subprocess.run( + f"docker ps -a --filter ancestor={image_id} --format '{{{{.ID}}}}'", + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + output = result.stdout.decode().strip() + if output: + return True # There are containers using the image + else: + return False # No containers are using the image except subprocess.CalledProcessError as e: - return False # Image does not exist, so it's not in use + print("Error:", e) + return False # An error occurred, possibly indicating the image is not in use + def delete_docker_image(image_id): try: @@ -59,3 +71,7 @@ def delete_docker_image(image_id): except subprocess.CalledProcessError as e: print("Error:", e) return False + + +def delete_unused_docker_images (): + subprocess.run("docker image prune -a", shell=True, check=True) \ No newline at end of file diff --git a/docker-manager/v1.1/Dockerfile b/docker-manager/v1.1/Dockerfile new file mode 100644 index 0000000..4e12f9f --- /dev/null +++ b/docker-manager/v1.1/Dockerfile @@ -0,0 +1,26 @@ +# +# Copyright 2018-2024 Hugo López-Fernández, Pedro M. Ferreira, Miguel +# Reboiro-Jato, Cristina P. Vieira, and Jorge Vieira +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM pegi3s/docker + +RUN apt-get -y update +RUN apt-get install -y python3 +RUN apt-get install -y python3-tk +COPY main.py /opt +COPY manageDocker.py /opt +WORKDIR /opt +ENTRYPOINT ["python3", "main.py"] \ No newline at end of file diff --git a/docker-manager/v1.1/README.md b/docker-manager/v1.1/README.md new file mode 100644 index 0000000..b711c40 --- /dev/null +++ b/docker-manager/v1.1/README.md @@ -0,0 +1,9 @@ +# This image belongs to a larger project called Bioinformatics Docker Images Project (http://pegi3s.github.io/dockerfiles) +## (Please note that the original software licenses still apply) + +This image facilitates the usage of docker-manager, a program with a graphical interface for managing Docker images. + +# Using the docker-manager image in Linux +You should run the following command: `docker run --rm -ti -e USERID=$UID -e USER=$USER -e DISPLAY=$DISPLAY -v /var/db:/var/db:Z -v /tmp/.X11-unix:/tmp/.X11-unix -v $HOME/.Xauthority:/home/developer/.Xauthority -v /var/run/docker.sock:/var/run/docker.sock -v /tmp:/tmp pegi3s/docker-manager` + +Because of its graphical interface you must run the `xhost +` command first. diff --git a/docker-manager/v1.1/main.py b/docker-manager/v1.1/main.py new file mode 100644 index 0000000..9b7da7b --- /dev/null +++ b/docker-manager/v1.1/main.py @@ -0,0 +1,108 @@ +import subprocess +import tkinter as tk +from tkinter import ttk +from manageDocker import delete_docker_image, get_docker_images + +def show_toast(instruction): + toast = tk.Toplevel() + toast.title("Toast") + toast.geometry("300x100") + tk.Label(toast, text=instruction, font=("Helvetica", 12)).pack(pady=20) + toast.after(2000, toast.destroy) # Close the toast after 2 seconds (2000 milliseconds) + +def delete_item(item_id): + my_tree.delete(item_id) + +def on_delete_button_click(item_id): + delete_item(item_id) + +def is_image_in_use(image_id): + try: + subprocess.run(f"docker inspect {image_id}", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return True # Image is in use + except subprocess.CalledProcessError as e: + return False # Image is not in use + +def delete_selected_item(): + selected_items = my_tree.selection() + for item_id in selected_items: + image_id = my_tree.item(item_id, "values")[1] # Get the Image ID from the selected item + if delete_docker_image(image_id): + delete_item(item_id) # Delete the item from the Treeview if the image was deleted + +def refresh_treeview(): + # Clear the existing items in the Treeview + for item in my_tree.get_children(): + my_tree.delete(item) + + # Fetch Docker images and populate the Treeview + docker_images = get_docker_images() + for image in docker_images: + status = is_image_in_use(image["IMAGE ID"]) + item_id = my_tree.insert("", "end", values=(image["REPOSITORY"], image["IMAGE ID"], image["CREATED"], image["SIZE"], status)) + +def delete_unused_images(): + docker_images = get_docker_images() + for image in docker_images: + status = is_image_in_use(image["IMAGE ID"]) + if not status: # If the image is not in use, delete it + delete_docker_image(image["IMAGE ID"]) + + refresh_treeview() # Refresh the Treeview after deleting unused images + +# Create the main window +window = tk.Tk() +window.title("Manage Docker Images") +window.geometry("1000x500") + +# Add text above the Treeview +title_label = tk.Label(window, text="Manage Docker Images", font=("Helvetica", 25)) +title_label.pack(pady=20) + +# Create a frame to hold the Treeview and the vertical scrollbar +tree_frame = tk.Frame(window) +tree_frame.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) + +# Create a Treeview for displaying items +my_tree = ttk.Treeview(tree_frame, columns=("Name", "ID", "Date", "Size", "Status"), show="headings", height=14) +my_tree.heading("Name", text="Name") +my_tree.heading("ID", text="Image ID") +my_tree.heading("Date", text="Date") +my_tree.heading("Size", text="Size") +my_tree.heading("Status", text="Status") +my_tree.column("Name", width=200, anchor="center") +my_tree.column("ID", width=150, anchor="center") +my_tree.column("Date", width=150, anchor="center") +my_tree.column("Size", width=100, anchor="center") +my_tree.column("Status", width=100, anchor="center") + + + +# Fetch Docker images and populate the Treeview +docker_images = get_docker_images() +for image in docker_images: + status = is_image_in_use(image["IMAGE ID"]) + item_id = my_tree.insert("", "end", values=(image["REPOSITORY"], image["IMAGE ID"], image["CREATED"], image["SIZE"], status)) + +# Create a vertical scrollbar +vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=my_tree.yview) +my_tree.configure(yscrollcommand=vsb.set) +vsb.pack(side="right", fill="y") + +# Increase the width of the entire treeview +my_tree.pack(expand=True, fill="both") + +# Create a frame for buttons at the bottom +button_frame = tk.Frame(window) +button_frame.pack(pady=10) + +refreshButton = tk.Button(button_frame, text="Refresh", command=refresh_treeview) +deleteUnusedImagesButton = tk.Button(button_frame, text="Delete Unused Images", command=delete_unused_images) +delete_button = tk.Button(button_frame, text="Delete Selected", command=delete_selected_item) + +refreshButton.pack(side="left", padx=10) +deleteUnusedImagesButton.pack(side="left", padx=10) +delete_button.pack(side="left", padx=10) + + +window.mainloop() diff --git a/docker-manager/v1.1/manageDocker.py b/docker-manager/v1.1/manageDocker.py new file mode 100644 index 0000000..cda7f89 --- /dev/null +++ b/docker-manager/v1.1/manageDocker.py @@ -0,0 +1,61 @@ +import subprocess + +def get_docker_images(): + try: + # Run the "docker image list" command and capture the output + result = subprocess.run("docker image list", shell=True, capture_output=True, text=True, check=True) + + # The output of the command is stored in the 'stdout' attribute of the 'result' object + docker_images_output = result.stdout + + # Split the input string into lines and skip the header line + lines = docker_images_output.strip().split('\n')[1:] + + # Create a list to store image details + docker_images = [] + + # Iterate through the lines and create objects for each image + for line in lines: + parts = line.split() + image_id = parts[2] # Image ID is at index 2 + repository = parts[0] + created = parts[4] + " " + parts[5] # Combine the "CREATED" and "TIME AGO" columns + size = parts[6] + + # Create a dictionary for each image and add it to the list + docker_image = { + "IMAGE ID": image_id, + "REPOSITORY": repository, + "CREATED": created, + "SIZE": size + } + docker_images.append(docker_image) + + return docker_images + except subprocess.CalledProcessError as e: + print("Error:", e) + +def check_images_usage(image_list): + image_status = {} + + for image in image_list: + image_id = image["IMAGE ID"] + is_used = is_image_in_use(image_id) + image_status[image_id] = is_used + + return image_status + +def is_image_in_use(image_id): + try: + subprocess.run(f"docker inspect {image_id}", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return True # Image exists and can be inspected, so it's in use + except subprocess.CalledProcessError as e: + return False # Image does not exist, so it's not in use + +def delete_docker_image(image_id): + try: + subprocess.run(f"docker rmi -f {image_id}", shell=True, check=True) + return True + except subprocess.CalledProcessError as e: + print("Error:", e) + return False