Docker/docker-find-dependants

From Omnia
Jump to navigation Jump to search

ref: https://raw.githubusercontent.com/RidiculousRichard/bash-dev-scripts/master/docker-find-dependants

ref: https://superuser.com/questions/1187156/with-docker-is-there-a-way-of-determining-what-images-rely-on-another-image

docker-find-dependants:

#!/bin/bash

# Script to determine what named images and containers are dependent on the specified image (or a containers image) from stdin or in the args
# An unmatchable input is ignored
# All of the following will be found:
#    Any image name / tag / name:tag / id that has a prefix in it that matches the input
#    Any other image that relies on the images specified
#    Any containers (running or not) that use any of these images

set -euf -o pipefail


specifiedParents=""
# Find all the parents
if [ "$#" -ge "1" ]; then
	# Parents are in args
	specifiedParents="$(printf " %s\n" "$@")"
else
	# Parents arrive via stdin
	while IFS= read -r inStr; do
		if [ -n "$inStr" ]; then
			inStr="${inStr#"${inStr%%[![:space:]]*}"}"
			specifiedParents="$(printf "%s\n %s" "$specifiedParents" "$inStr")"
		fi
	done
fi

# Check there is some input
if [ -z "$specifiedParents" ]; then
	printf "%s" "Nothing specified to search for; images/containers must be specified as either args or on the stdin" >&2 
	exit 1
fi

# Collect the container and image info
containersData="$(docker ps -a --format 'Container {{.ID}} {{.Image}} {{.Names}} {{.Labels}}' --no-trunc)"
allImagesData="$(docker images -a --format 'Image {{.ID}} {{.Repository}}:{{.Tag}} {{.Repository}} {{.Tag}}' --no-trunc | sed 's/ [^:]*:/ /')"
namedImagesData="$(docker images --format 'Image {{.ID}} {{.Repository}}:{{.Tag}}' --no-trunc | sed 's/ [^:]*:/ /')"

# Check to see if you can find a matching container
matchedContainerIds=""
parentImageIds=""
while IFS= read -r aParent; do
	if [ -z "$aParent" ]; then
		continue
	fi
	
	# Use space to ensure matching starts at the beginning of a field
	aContainerId=" $(printf "%s" "$containersData" | grep -F "$aParent" | awk '{print $3}' || true)"
	if [ "$aContainerId" != " " ]; then
		# A container matched so use the image ID
		matchedContainerIds="$(printf "%s\n%s" "$matchedContainerIds" "$aContainerId")"
	fi

	# Also check images
	while IFS= read -r parentImageId; do
		if [ -z "$parentImageId" ]; then
			continue
		fi
		# Add the full ID to the parentImageIds, including a space to assist matching later
		parentImageIds="$(printf "%s%s\n" "$parentImageIds" " $parentImageId")"
	done <<< "$(printf "%s" "$allImagesData" | grep -F "$aParent" | awk '{print $2}')"
done <<< "$(printf "%s\n" "$specifiedParents")"

# Stop if there are no parents or containers
if [ -z "$parentImageIds" ] && [ -z "$matchedContainerIds" ]; then
	exit 0
fi

# Deduplicate
parentImageIds="$(printf "%s" "$parentImageIds" | LC_ALL=C sort -u)"

# Find descendent images
descendentImages=""
while IFS= read -r imageData; do
	anImageId="$(printf "%s" "$imageData" | awk '{print $2}')"

	# Check to see if this ID is a descendent of parentImageIds by imageInfo by moving through it's lineage
	areDescendents=""
	imageIdsChecked=""
	while true; do
		# Record that anImageId is being checked; including the space at the start to assist matching later
		imageIdsChecked="$(printf "%s%s\n" "$imageIdsChecked" " $anImageId")"

		if printf "%s" "$descendentImages" | grep -q -F "$anImageId"; then
			# Already determined that anImageId is a descendent of parentImageIds so TheImage is too
			areDescendents="true"
			break;
		else
			if printf "%s" "$parentImageIds" | grep -q -F " $anImageId"; then
				# TheImage is a descendent of parentImageIds
				areDescendents="true"
				break;
			fi

			# Move onto the parent of anImageId
			anImageId="$(docker inspect --format '{{.Parent}}' "$anImageId" | sed 's/.*://')"
			if [ -z "$anImageId" ]; then
				# Reached the end of the line; abandon all hope ye who enter here
				break;
			fi
		fi
	done

	# Add descendents to the descendentImages
	if [ -n "$areDescendents" ]; then
		descendentImages="$(printf "%s%s\n" "$descendentImages" "$imageIdsChecked")"
	fi
done <<< "$(printf "%s" "$allImagesData")"

# Identify any named images that are descendents of the parentImageIds
printf "%s\n" "$namedImagesData" | while IFS= read -r imageData; do
	thisImageId="$(printf "%s" "$imageData" | awk '{print $2}')"
	if printf "%s" "$descendentImages" | grep -q -F " $thisImageId"; then
		# We've found a descendent
		printf "%s\n" "$imageData"
	fi
done

# Identify containers that rely on images that are descendents of the parentImageIds
printf "%s\n" "$containersData" || while IFS= read -r containerData; do
	# Check to see if this container is dependant on a dependant image
	thisImageId="$(printf "%s" "$containerData" | awk '{print $3}')"
	if printf "%s" "$descendentImages" | grep -q -F " $thisImageId"; then
		# We've found a descendent
		printf "%s\n" "$containerData"
		continue
	fi

	# Check to see if this container itself has been matched
	thisContainerId="$(printf "%s" "$containerData" | awk '{print $2}')"
	if printf "%s" "$matchedContainerIds" | grep -q -F " $thisContainerId"; then
		# We've matched a container
		printf "%s\n" "$containerData"
		continue
	fi	
done